Limits and caps
Every hard numeric limit relevant to authoring a mod, gathered into one table. Each row names the field it bounds and the file:line it was verified against on 2026-07-27 — source wins if this page and the code ever disagree; treat a mismatch as a documentation bug, not a schema change to make.
Table state (TableSnapshot)#
The replicated, persisted state of a live table — what any mod, script or save file's objects collection
is bounded by, regardless of how the objects got there.
| Collection | Cap | Source |
|---|---|---|
objects |
5000 | tableObjects.ts, tableSnapshotSchema.objects |
zones |
500 | tableObjects.ts, tableSnapshotSchema.zones |
snapPoints |
1000 | tableObjects.ts, tableSnapshotSchema.snapPoints |
vectorLines |
10000 | tableObjects.ts, tableSnapshotSchema.vectorLines |
decals |
5000 | tableObjects.ts, tableSnapshotSchema.decals |
textLabels |
1000 | tableObjects.ts, tableSnapshotSchema.textLabels |
joints |
3000 | tableObjects.ts, tableSnapshotSchema.joints |
ui.elements |
2000 | tableObjects.ts, tableUiStateSchema.elements |
eventLog |
5000 | tableObjects.ts, tableSnapshotSchema.eventLog |
All nine are enforced by tableSnapshotSchema/tableUiStateSchema — a snapshot exceeding any of them fails
Zod parsing outright, on every peer, the same way. See Object state for
what each collection is.
Per-entity#
| Field | Cap | Source |
|---|---|---|
secretMetadata |
2 KB (2,048 bytes of JSON.stringify output, UTF-8) |
tableObjects/secretMetadata.ts, MAX_SECRET_METADATA_BYTES |
secretMetadata is the only per-entity field with a hard size cap; metadata has none, and both are bounded
in practice by the 5,000-entity ceiling above. The cap is deliberately an order of magnitude tighter than the
16 KB mod saved-data value cap, because saved data is one blob per mod while this rides on every entity of
every snapshot. Over the cap, the object fails schema validation — except on load, where
migrateTableSnapshot() prunes the field instead, so a hand-edited save cannot wedge a room. See
Object state.
The legacy mod-setup format#
| Field | Cap | Source |
|---|---|---|
templates |
500 | tableObjects.ts, modSetupSchema.templates |
objects (template references / inline definitions) |
500 | tableObjects.ts, modSetupSchema.objects |
These two belong to the legacy mod-setup setup-file format (templatesVersion/templates/objects),
not the modern edit-scene format every mod authored through Edit Mode produces today. See
Setup JSON for the distinction. The modern format's objects[] has no
separate array-level cap of its own beyond the overall setup-file byte limit below — it is bounded by size,
not count.
Manifest and file-tree#
| Field | Cap | Source |
|---|---|---|
assets[] (declared asset paths) |
200 | modManifest.ts, modManifestSchema.assets |
assetManifest[] |
200 | modManifest.ts, modManifestSchema.assetManifest |
soundSets[] |
64 | modManifest.ts, modManifestSchema.soundSets |
Published file-tree files[] (modFileTreeSchema) |
2000 | modFileTree.ts |
Local draft files[] (modProjectDraftSchema, a separate schema — same number, coincidentally) |
2000 | modManifest.ts |
| Per-asset size | 50 MB (50 * 1024 * 1024 bytes) |
modManifest.ts; enforced twice more server-side — see Assets |
See Required and conventional files § the file-tree envelope
for the full ModFileEntry shape these counts bound, and Assets for the
allowlist the 50 MB cap applies alongside.
Setup options (manifest.setupOptions)#
The typed controls a mod declares for a host to answer before the table starts. Verified against
packages/shared/src/modManifest.ts and packages/shared/src/settingControls.ts on 2026-09-04.
| Field | Cap | Source |
|---|---|---|
setupOptions[] |
12 options | modManifest.ts, MOD_MAX_SETUP_OPTIONS |
setupOptions[].key |
1-64 chars, and must match /^[a-z][a-z0-9_]*$/ |
modManifest.ts, MOD_SETUP_OPTION_KEY_PATTERN |
setupOptions[].label |
1-80 chars | modManifest.ts, modSetupOptionSchema.label |
setupOptions[].help |
240 chars | modManifest.ts, modSetupOptionSchema.help |
setupOptions[].default (string form) |
2000 chars | modManifest.ts, modSetupOptionSchema.default |
setupOptions[].maxLength (text only) |
an integer from 1 to 2000 | modManifest.ts, modSetupOptionSchema.maxLength |
setupOptions[].options[] (select choices) |
32 choices | modManifest.ts, MOD_MAX_SETUP_OPTION_CHOICES (= settingControls.ts, PLUGIN_MAX_SETTING_OPTIONS) |
setupOptions[].options[].value / .label |
1-120 chars each | settingControls.ts, pluginSettingOptionSchema |
The option count and choice-count caps are the plugin-settings numbers reused rather than re-chosen, so a
control that moves between the two surfaces does not meet a different ceiling. Every one of these is enforced
by modManifestSchema, so a manifest over any of them fails registration outright.
⚠ The declaration is validated today; nothing consumes the answers yet. See
setupOptions for what is and is not wired.
The mod-project draft's raw text fields#
Separate from the file-tree caps above: modProjectDraftSchema (modManifest.ts) is the
server-persisted editor draft record for a mod-in-progress, and its three big text fields each have
their own independent byte cap (measured in JavaScript string length, effectively UTF-16 code units):
| Field | Cap | Source |
|---|---|---|
Manifest text (manifestText) |
1 MB (1,000,000 chars) | modManifest.ts |
Setup text (setupText) |
5 MB (5,000,000 chars) | modManifest.ts |
Script text (scriptText) |
2 MB (2,000,000 chars) | modManifest.ts |
These bound the draft's raw JSON/JS text while you are authoring — they are independent of, and looser than, the per-file 50 MB cap and the 2000-file tree cap above, which apply once content is committed to the file-tree representation for publish.
Table Scripting scene scripts — a different surface, included for completeness#
⚠ These three numbers belong to Table Scripting (the
TypeScript scripts authored in Edit Mode, attached to a scene or an entity) — not to a mod's own
entry.script, which has no dedicated source/compiled-length cap beyond the draft-level scriptText cap
above. Listed here because a scene's scripts travel inside the same edit-scene setup document a mod
publishes, so they are still a real limit a mod author can hit.
| Field | Cap | Source |
|---|---|---|
| Scene script source (author TypeScript) | 256 KB (262,144 chars) | scripting.ts, SCENE_SCRIPT_MAX_SOURCE_LENGTH |
| Scene script compiled output (transpiled JS) | 512 KB (524,288 chars) | scripting.ts, SCENE_SCRIPT_MAX_COMPILED_LENGTH |
| Scripts per scene | 64 | scripting.ts, SCENE_SCRIPT_MAX_COUNT |
See Sandbox limits for what else constrains a script beyond sheer size.
See also#
- Object state — what the nine
TableSnapshotcollections are. - Setup JSON — the modern vs. legacy setup-file formats these caps span.
- Required and conventional files — the file-tree envelope in full.
- Assets — the extension allowlist alongside the 50 MB per-asset cap.
- Manifest reference — every manifest field, including the capped arrays above.
- Mod scripting API —
TableSnapshot, the shape every collection cap on this page applies to. - Mod hooks and capabilities —
ModTurnStartPayload.actionLimit, a per-turn cap that is not on this page.
