Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/uipath-coded-apps/references/sdk/data-fabric.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Data Fabric does NOT behave like a typical RDBMS. These server behaviors are inv
6. **No `IsNull` filter operator.** The server can't ask "where field is null" (`QueryFilterOperator` has no such member). Filter client-side after fetching, or design with explicit non-null sentinels.
7. **Aggregates require server-side `aggregates` + `groupBy`.** Don't fetch raw rows and `.length` / `.reduce` client-side — every list call returns one page (see [pagination.md](pagination.md)), so `result.items.length` after `queryRecordsById({ filter })` returns at most one page's worth, no matter how many rows match. Use `totalCount` for cardinality, `aggregates: [{ function: 'COUNT', field: 'Id' }]` (with `groupBy` for per-bucket counts) for chart data.
8. **File-type fields (`fieldDisplayType === 'File'`) aren't strings.** The record carries only metadata (`{ id, name, size, contentType }`); stringifying gives `"[object Object]"`. To display, call `entities.downloadAttachment(entityId, recordId, fieldName)` → `Blob` → `URL.createObjectURL` for an `<img src>`. **Neither `contentType` nor filename extension is reliable for detecting kind** — DF often returns `application/octet-stream`, and the stored `name` is frequently a bare UUID with no extension. To decide inline render vs download link, either (a) sniff the blob's magic bytes after download (PNG starts `89 50 4E 47`, JPEG `FF D8 FF`, GIF `47 49 46 38`, PDF `25 50 44 46`), or (b) optimistically attempt `<img src={objectUrl}>` and swap to a download link in `onError`. Writes go through `uploadAttachment(...)`, not `insertRecordById` / `updateRecordById`.
9. **`MULTILINE_MAX` fields return a size marker on list/query reads.** `getAllRecords` / `queryRecordsById` return a string starting `HasValue=true Length=N` (live form: `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`), never the content — only `getRecordById` returns the full value (SDK 1.5.2+, v2 read endpoint). Never render or persist the marker as data, and never echo it back through `updateRecordById` / `updateRecordsById` — the server accepts it as a normal value and silently destroys the real content; omit the key instead. The type accepts no filters or `sortOptions` (server 400: *"Field '<name>' is of type MULTILINE_MAX and cannot be used in filters."*). `lengthLimit` is a UTF-16 **byte** budget (max 131072 ≈ 65,536 chars).
9. **`MULTILINE_MAX` fields return only a preview on list/query reads.** `getAllRecords` / `queryRecordsById` return either the value truncated to 10,000 characters with a `...[Truncated]` suffix or a size marker starting `HasValue=true Length=N` (sometimes with a trailing hint), depending on the tenant; encrypted fields always return `HasValue=true Encrypted=true`. Only `getRecordById` returns the guaranteed full value (SDK 1.5.2+, v2 read endpoint). Never render or persist a preview as data, and never echo it back through `updateRecordById` / `updateRecordsById` — the server accepts it as a normal value and silently destroys the real content; omit the key instead. The type accepts no filters or `sortOptions` (server 400: *"Field '<name>' is of type MULTILINE_MAX and cannot be used in filters."*). `lengthLimit` is a UTF-16 **byte** budget (max 131072 ≈ 65,536 chars).

## Three paths require choice-value translation — don't miss any

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Do not change field types, create federated entities, or write federated records

20. **`records import` supports Basic types only.** `CHOICE_SET_*`, `RELATIONSHIP`, `FILE`, `AUTO_NUMBER` columns are ignored on import — optional columns land as `null`; **`isRequired` columns without a `defaultValue` fail the whole row** (`ErrorFileLink` entry per row). Sequence: (1) `entities get` → list unsupported columns; (2) tell the user which columns will be skipped, and which rows will fail because a required unsupported column has no default; (3) offer `records insert --file <json>` (+ `files upload` for FILE) as the alternative; (4) invoke only after explicit confirmation. See [`bulk-import.md` → Complex Field Types Not Supported](bulk-import.md#complex-field-types-not-supported).

21. **`MULTILINE_MAX` — marker reads, no filter/sort.** `records list` / `records query` return a size marker (`HasValue=true Length=N`)full value only via `records get`. Never echo the marker back through `records update` — the server accepts it as a normal value and destroys the real content; omit the key instead. No filter/sort support (400). `lengthLimit` is a UTF-16 byte budget (max 131072 ≈ 65,536 chars). Full contract: [`entity-schema.md` → MULTILINE_MAX](entity-schema.md#multiline_max-fields) + [`records-query.md` → MULTILINE_MAX](records-query.md#multiline_max-fields--marker-vs-full-content).
21. **`MULTILINE_MAX` — preview reads, no filter/sort.** `records list` / `records query` return a preview, either content truncated to 10,000 characters with a `...[Truncated]` suffix or a size marker (`HasValue=true Length=N`), depending on the tenant; full value only via `records get`. Never echo a preview back through `records update` — the server accepts it as a normal value and destroys the real content; omit the key instead. No filter/sort support (400). `lengthLimit` is a UTF-16 byte budget (max 131072 ≈ 65,536 chars). Full contract: [`entity-schema.md` → MULTILINE_MAX](entity-schema.md#multiline_max-fields) + [`records-query.md` → MULTILINE_MAX](records-query.md#multiline_max-fields--preview-vs-full-content).

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
> **Preview-then-confirm gate (data-fabric.md Rule 14).** Before invoking `entities create` — or any `entities update` that adds, updates, or removes fields — render the full proposed schema (entity name, displayName, description, every field with normalized type and all extras) as a table or formatted JSON block and wait for explicit user approval. Don't run the CLI until the user confirms.

```bash
uip df entities create "MyEntity" \

Check warning on line 8 in skills/uipath-platform/references/data-fabric/entity-schema.md

View workflow job for this annotation

GitHub Actions / skills/uipath-platform

Possibly stale `uip df entities create MyEntity` (valid prefix: `df entities create`)
--body '{
"displayName": "My Entity",
"description": "Optional description",
Expand All @@ -32,7 +32,7 @@
|---|---|---|
| `STRING` | NVARCHAR | Short text (≤4000 chars via `lengthLimit`) |
| `MULTILINE_TEXT` | NVARCHAR(MAX) | Long text (≤10000 chars via `lengthLimit`) |
| `MULTILINE_MAX` | NVARCHAR(MAX) | Very large text (`lengthLimit` = UTF-16 byte budget, 1–131072; default 128 KB ≈ 65,536 chars max). No filter/sort; list/query reads return a size marker — see [MULTILINE_MAX fields](#multiline_max-fields) |
| `MULTILINE_MAX` | NVARCHAR(MAX) | Very large text (`lengthLimit` = UTF-16 byte budget, 1–131072; default 128 KB ≈ 65,536 chars max). No filter/sort; list/query reads return a preview, not the guaranteed full value — see [MULTILINE_MAX fields](#multiline_max-fields) |
| `DECIMAL` | DECIMAL | All numbers — `decimalPrecision: 0` for whole; `2` for money |
| `BOOLEAN` | BIT | true/false |
| `DATE` | DATE | Date only |
Expand Down Expand Up @@ -80,11 +80,11 @@
Very large text. Contract differs from `MULTILINE_TEXT`:

1. **Not filterable, not sortable.** Any `queryFilters` or `sortOptions` entry naming a `MULTILINE_MAX` field → 400: *"Field '<name>' is of type MULTILINE_MAX and cannot be used in filters."* / *"Sort field '<name>' is of type MULTILINE_MAX and cannot be used for sorting."* Never offer the field in filter/sort predicates. See [filter contract](filter-platform-contract.md#operator-support-by-field-type).
2. **List/query reads return a size marker, not content.** `records list` / `records query` return a string starting `HasValue=true Length=N` (live form: `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`); only `records get <entity-id> <record-id>` returns the full value. Read + write-back rules in [records-query.md → MULTILINE_MAX fields](records-query.md#multiline_max-fields--marker-vs-full-content).
2. **List/query reads return a preview, not the guaranteed full value.** Depending on the tenant, `records list` / `records query` return either the value truncated to 10,000 characters with a `...[Truncated]` suffix, or a size marker starting `HasValue=true Length=N` (sometimes with a trailing hint). Encrypted fields always read back `HasValue=true Encrypted=true`. Only `records get <entity-id> <record-id>` returns the full value. Read + write-back rules in [records-query.md → MULTILINE_MAX fields](records-query.md#multiline_max-fields--preview-vs-full-content).
3. **On a 400 from `entities create` / `addFields` naming the type**, surface the error verbatim — do NOT retry or silently substitute `MULTILINE_TEXT` (Rule 18).

```bash
uip df entities create "Documents" \

Check warning on line 87 in skills/uipath-platform/references/data-fabric/entity-schema.md

View workflow job for this annotation

GitHub Actions / skills/uipath-platform

Possibly stale `uip df entities create Documents` (valid prefix: `df entities create`)
--body '{"fields":[{"name":"Title","type":"STRING","isRequired":true},{"name":"Body","type":"MULTILINE_MAX"}]}' \
--output json
```
Expand Down Expand Up @@ -161,7 +161,7 @@
| `decimalPrecision` | `DECIMAL` — `0` whole, `2` money | 0–10 |

```bash
uip df entities create "Orders" \

Check warning on line 164 in skills/uipath-platform/references/data-fabric/entity-schema.md

View workflow job for this annotation

GitHub Actions / skills/uipath-platform

Possibly stale `uip df entities create Orders` (valid prefix: `df entities create`)
--body '{
"fields": [
{"name": "ProductName", "type": "STRING", "lengthLimit": 500, "isRequired": true},
Expand Down
13 changes: 9 additions & 4 deletions skills/uipath-platform/references/data-fabric/records-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@ Response wrapper: `{ Result, Code: "RecordList" | "RecordQuery", Data: { Items,
- **`Data.NextCursor` is an object `{ "Value": "<base64-string>" }`, not a flat string.** Pass `Data.NextCursor.Value` to `--cursor` on the next call (unwrap one level). Passing the whole `NextCursor` object errors out.
- Use `Data.HasNextPage` to check if more records exist. Stop when it's `false`.

## MULTILINE_MAX Fields — Marker vs Full Content
## MULTILINE_MAX Fields — Preview vs Full Content

`records list` and `records query` do NOT return `MULTILINE_MAX` content. Each such field comes back as a size marker string starting `HasValue=true Length=N` — live form: `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`. Only single-record read returns the full content:
`records list` and `records query` do NOT reliably return `MULTILINE_MAX` content. Each such field comes back as a **preview**, and which form you get depends on the tenant:

- the value truncated to 10,000 characters with a `...[Truncated]` suffix (a value at or under 10,000 characters can come back whole), or
- a size marker starting `HasValue=true Length=N`, sometimes with a trailing hint such as `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`.

An encrypted `MULTILINE_MAX` field always reads back as `HasValue=true Encrypted=true` and never returns content. Do not branch on which form you got; treat any of them as a preview. Only the single-record read returns the full value:

```bash
uip df records get <entity-id> <record-id> --output json
```

1. **Never treat the marker as the value.** Don't display, compare, or persist `"HasValue=true Length=N"` as field content — fetch via `records get` first.
2. **Never write the marker back.** A `records update` body built by echoing a record from `list` / `query` overwrites the real content with the literal marker string — verified: the server accepts it as a normal value, `Result: Success`, content silently destroyed. Omit `MULTILINE_MAX` keys from update bodies unless intentionally replacing the content.
1. **Never treat a preview as the value.** Don't display, compare, or persist what `list` / `query` returned for the field; fetch via `records get` first. A truncated preview is the more dangerous form because it looks like ordinary content.
2. **Never write a preview back.** A `records update` body built by echoing a record from `list` / `query` overwrites the real content with the preview — verified: the server accepts it as a normal value, `Result: Success`, content silently destroyed. With a truncated preview the stored value keeps its first 10,000 characters and loses the rest, which is far harder to notice than a marker string. Omit `MULTILINE_MAX` keys from update bodies unless intentionally replacing the content.
3. **No filter, no sort.** `queryFilters` / `sortOptions` naming a `MULTILINE_MAX` field → 400: *"Field '<name>' is of type MULTILINE_MAX and cannot be used in filters."* / *"Sort field '<name>' is of type MULTILINE_MAX and cannot be used for sorting."* Surface verbatim (data-fabric.md Rule 18); don't retry with other operators. Full type contract: [entity-schema.md → MULTILINE_MAX fields](entity-schema.md#multiline_max-fields).

## Pagination
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
task_id: skill-datafabric-integration-multiline-max
description: >
Integration: MULTILINE_MAX lifecycle — create an entity with a MULTILINE_MAX
field, insert ~20k-char content, observe the size marker on records list,
retrieve full content via records get, and surface the server-side filter
rejection verbatim without retrying (Rules 18 + 21). Requires the
MultilineMax feature flag enabled on the test tenant.
field, insert ~20k-char content, observe that records list returns only a
preview of it, retrieve the full content via records get, and surface the
server-side filter rejection verbatim without retrying (Rules 18 + 21).
Requires the MultilineMax feature enabled on the test tenant.
tags: [uipath-platform, data-fabric, integration, entities, records, mode:build, lifecycle:setup]

run_limits:
Expand Down Expand Up @@ -72,25 +72,34 @@ success_criteria:
pass_threshold: 1.0

- type: llm_judge
description: "Agent explained the marker-vs-full contract and surfaced the filter rejection without silent workarounds"
description: "Agent explained the preview-vs-full contract and surfaced the filter rejection without silent workarounds"
include_agent_output: true
prompt: |
The agent worked against a Data Fabric entity whose Body field is
MULTILINE_MAX. Platform contract: `records list` / `records query`
return a size marker string starting "HasValue=true Length=N" (with a
trailing hint about Get Entity Record By Id) for such fields; only
`records get` returns the full content; server-side filters on the
return only a PREVIEW of such a field, never the guaranteed full value.
The preview takes one of two forms depending on the tenant, and BOTH are
correct platform behaviour:
- the content truncated to 10,000 characters with a "...[Truncated]"
suffix, or
- a size marker string starting "HasValue=true Length=N", sometimes
with a trailing hint about Get Entity Record By Id.
Only `records get` returns the full content. Server-side filters on the
field are rejected with HTTP 400 ("Field 'Body' is of type
MULTILINE_MAX and cannot be used in filters.").

Read the agent's final response. Score on three points:

a) The agent showed that `records list` returned a size marker (not the
content) for Body, and showed the full ~20,000-char content (or a
a) The agent showed what `records list` returned for Body and it was a
preview in either of the two forms above (i.e. not the full ~20,000
characters), and showed the full ~20,000-char content (or a
clearly-labelled excerpt of it) obtained via the single-record read.
b) The agent explained the difference correctly (list/query return a
marker; the single-record read returns full content) — not a wrong
theory like truncation, encoding, or a bug.
b) The agent explained the difference correctly: list/query give a
bounded preview and the single-record read gives the full content.
Describing the preview as truncated content, or as a size marker,
is correct as long as it matches what the tenant actually returned.
Score this point false only for a wrong theory, such as claiming
list returns the complete value, or blaming encoding or a bug.
c) For the "contains lorem" filter request, the agent either surfaced
the server's rejection verbatim (real error text/code) or stated
up-front that MULTILINE_MAX is not filterable citing the platform
Expand Down
Loading