Every string or byte value in a document is capped at 5,120 bytes (max_field_value_size), including properties that no index uses. Ordinary long text, like a pull request description or release notes, often goes over that, even though the whole transition may be 20,480 bytes. We'd like non-indexed properties to be allowed to go up to what fits in a transition, still bounded by the schema and billed per byte.
We hit this building Dash Forge, a git forge that runs on Platform.
Expected Behavior
A non-indexed string or byte-array property can hold a value as large as its schema allows (maxLength / maxBytes / maxItems), up to a new, larger system limit (for example 16 KiB), as long as the whole state transition still fits max_state_transition_size. Storage and processing are billed per byte, as they are today.
Current Behavior
Impact on applications. We sampled GitHub on 2026-10-04 (pull request bodies, UTF-8 bytes, newest first):
| Repository |
Sample |
Bodies over 5,120 B |
Largest |
Over 16 KiB |
dashpay/dash |
500 most recent PRs (#7261–#7801) |
80 (16 %) |
36,231 B |
4 |
dashpay/platform |
100 most recent PRs (#5136–#5262) |
56 (56 %) |
25,420 B |
6 |
So a forge, a forum, a docs app, or anything that stores user-written text runs into this regularly. A limit around 16 KiB would cover most of these bodies, though not all of them.
Our workaround. When a text is longer than its field, we store the full text as a separate content-addressed artifact, split across several chunk documents, plus a manifest. The field keeps the first part of the text and a trailer that names the artifact (forge-v2.md §6.3). This costs extra writes and fees for the author, and extra requests for every reader. Because our contract only lets maintainers write artifacts, other users still can't post a long text. Before this, our importer cut the text and linked to the original on GitHub, which ties the content to a host we don't control.
Possible Solution
A suggestion:
- Keep 5,120 B (or the current rules) for values that are indexed or that become a GroveDB key.
- Add a separate, larger limit for non-indexed
string and byte-array properties, for example 16,384 B. The real bound is still max_state_transition_size, and the schema's own maxLength / maxBytes / maxItems keep fee estimation bounded as it is now.
- The
entryPayload check of indexOnly types reuses max_field_value_size as its cap (try_from_schema/common/mod.rs#L3800-L3806), so it would probably keep the current value under its own name.
- New protocol version only. Existing contracts and documents are unchanged, and a contract can use the larger size by adding a new optional property in an update.
We may be missing a reason the per-value cap must stay at 5,120 B for non-indexed data (storage layout, proof size, fee estimation). If there is one, it would help to have it documented next to the limit.
Alternatives Considered
- Splitting text into a typed array of strings. From protocol version 14 the 5,120-byte limit applies to each element, not to the list (typed-arrays.md#L63). That works, but every writer and reader has to split and rejoin the text at UTF-8 boundaries, and
maxBytes / propertyConstraints can't treat it as one text.
- Several fixed fields (
body1, body2, ...). The same problem, and it uses up schema properties.
- A separate artifact (our current workaround). It costs more, needs more requests and needs permission to write artifacts.
- Storing it off Platform. That gives up the proofs.
Additional Context
- Platform 5.0.0-beta.1 (protocol version 14) on a devnet (sakura).
- SDKs:
@dashevo/evo-sdk and @dashevo/wasm-sdk 5.0.0-beta.1, and rs dash-sdk / dpp at tag v5.0.0-beta.1.
- Sample method:
GET /repos/{owner}/{repo}/pulls?state=all&sort=created&direction=desc, measuring the UTF-8 byte length of body.
Every string or byte value in a document is capped at 5,120 bytes (
max_field_value_size), including properties that no index uses. Ordinary long text, like a pull request description or release notes, often goes over that, even though the whole transition may be 20,480 bytes. We'd like non-indexed properties to be allowed to go up to what fits in a transition, still bounded by the schema and billed per byte.We hit this building Dash Forge, a git forge that runs on Platform.
Expected Behavior
A non-indexed
stringor byte-array property can hold a value as large as its schema allows (maxLength/maxBytes/maxItems), up to a new, larger system limit (for example 16 KiB), as long as the whole state transition still fitsmax_state_transition_size. Storage and processing are billed per byte, as they are today.Current Behavior
max_field_value_size: 5120in protocol version 14's limits: system_limits/v4.rs#L97. The transition limit ismax_state_transition_size: 20480: #L108.Value::has_data_larger_than(rs-platform-value/src/lib.rs#L1567-L1572). A value over it fails withDocumentFieldMaxSizeExceededError(10417, codes.rs#L167). The book documents it the same way (contract-keywords.md#L312, property-schemas.md#L30).maxLength≤ 63, byte arraysmaxItems≤ 255: indexes.md#L90-L94). So in practice the 5,120-byte cap only limits non-indexed values.Impact on applications. We sampled GitHub on 2026-10-04 (pull request bodies, UTF-8 bytes, newest first):
dashpay/dashdashpay/platformSo a forge, a forum, a docs app, or anything that stores user-written text runs into this regularly. A limit around 16 KiB would cover most of these bodies, though not all of them.
Our workaround. When a text is longer than its field, we store the full text as a separate content-addressed artifact, split across several chunk documents, plus a manifest. The field keeps the first part of the text and a trailer that names the artifact (forge-v2.md §6.3). This costs extra writes and fees for the author, and extra requests for every reader. Because our contract only lets maintainers write artifacts, other users still can't post a long text. Before this, our importer cut the text and linked to the original on GitHub, which ties the content to a host we don't control.
Possible Solution
A suggestion:
stringand byte-array properties, for example 16,384 B. The real bound is stillmax_state_transition_size, and the schema's ownmaxLength/maxBytes/maxItemskeep fee estimation bounded as it is now.entryPayloadcheck ofindexOnlytypes reusesmax_field_value_sizeas its cap (try_from_schema/common/mod.rs#L3800-L3806), so it would probably keep the current value under its own name.We may be missing a reason the per-value cap must stay at 5,120 B for non-indexed data (storage layout, proof size, fee estimation). If there is one, it would help to have it documented next to the limit.
Alternatives Considered
maxBytes/propertyConstraintscan't treat it as one text.body1,body2, ...). The same problem, and it uses up schema properties.Additional Context
@dashevo/evo-sdkand@dashevo/wasm-sdk5.0.0-beta.1, and rsdash-sdk/dppat tagv5.0.0-beta.1.GET /repos/{owner}/{repo}/pulls?state=all&sort=created&direction=desc, measuring the UTF-8 byte length ofbody.