# Limits and errors

> Every trulyform definition limit, how save errors differ from publish issues, the MCP error codes, and how to fix the most common problems.

## What you need to know

- trulyform checks a form twice. **Saving** rejects a definition with the wrong shape and saves nothing. **Publishing** also requires the draft to have no `issues`.
- A draft can be saved with issues, such as an untitled question, so you can build a form in several steps.
- Every error names where the problem is: a path in the definition for save errors, a `nodeId` for publish issues.
- The limits below are hard limits.

## Limits

| Limit | Value | Notes |
| --- | --- | --- |
| `definition_bytes` | 2,000,000 | Maximum size of the whole definition as UTF-8 JSON. |
| `fields` | 200 | Questions per form. |
| `options_per_field` | 250 | Choices per question (matrix columns included). |
| `rows_per_field` | 100 | Rows per matrix question. |
| `rules` | 300 | Logic rules per form. |
| `conditions_per_rule` | 20 | Conditions per logic rule. |
| `calculations` | 100 | Calculations per form. |
| `expression_depth` | 12 | Nesting depth of one calculation expression. |
| `calculation_name_length` | 100 | Characters per calculation name. |
| `hidden_fields` | 30 | Hidden URL parameters captured per form. |
| `text_length` | 10,000 | Characters per rich-text text node. |
| `label_length` | 2,000 | Characters per choice or row label. |
| `button_length` | 100 | Characters per button label. |
| `max_files_per_answer` | 10 | Largest maxFiles setting. The respondent can upload no more than the form owner's plan allows: Free 1 file, Pro 10 files, Business 10 files. |
| `max_file_megabytes` | 10 | Largest file a respondent can upload, in MB, whatever maxSizeMB says. |

### Plan limits

Logic, calculations and every question type are available on every plan. These depend on the form owner's plan:

| Plan | Files per answer | File storage | Partial responses | Customer logo |
| --- | --- | --- | --- | --- |
| Free | 1 | 10 MB | No | No |
| Pro | 10 | 1 GB | Yes | No |
| Business | 10 | 10 GB | Yes | Yes |

Each person can make up to 120 MCP requests per minute.

## Save errors

When the shape is wrong, `save_form_draft` returns `validation_failed` with an `errors` object that maps a path to its messages:

```json title="A save error"
{
    "code": "validation_failed",
    "message": "Correct the indicated fields and try again.",
    "errors": { "fields.0.kind": ["The selected fields.0.kind is invalid."] }
}
```

Paths count from the definition: `fields.0.kind` is the `kind` of the first question. Paths of rich-text errors start with `definition.`, such as `definition.fields.0.title`, and point at the same place. The error's `docs` list names the sections that explain it; pass one to `read_docs`.

| Message | Cause | Fix |
| --- | --- | --- |
| The selected fields.0.kind is invalid. | Not one of the question types | Use a kind from [Question types](https://trulyform.com/docs/question-types.md). |
| The fields.0.id field must be a valid UUID. | The ID is not a UUIDv7 | Use a UUIDv7. See [Identifiers](https://trulyform.com/docs/form-definition.md#identifiers). |
| The fields.0.id field must be lowercase. | Upper-case letters in the ID | Write IDs in lower case. |
| The fields.0.id field has a duplicate value. | Two questions share an ID | Give each one its own ID. |
| Every question, choice, row, rule, and calculation needs a unique identifier. | An ID is used twice, for example by a choice and a question | Use a fresh ID for each. |
| Use supported text formatting. | A title or description is a string, or uses an unknown node or mark | Write it as [rich text](https://trulyform.com/docs/form-definition.md#rich-text). |
| Use paragraphs with inline text and answer references. | Rich-text nodes in the wrong place, such as text directly in the doc | Put text inside a paragraph inside the doc. |
| The fields.0.settings field must be an array. | `settings` contains a key that is not a setting, such as `options` or `required` | Remove the key. The allowed keys are in [Settings](https://trulyform.com/docs/question-types.md#settings). |
| The definition field must be an array. | An unknown top-level key, such as `pages` | Send only the [top-level keys](https://trulyform.com/docs/form-definition.md#top-level-keys). |
| The fields.0.required field must be true or false. | A string such as "true" | Send a JSON boolean. |
| The theme.primary field format is invalid. | A color that is not #RRGGBB | Use a six-digit hex color, such as #2563eb. |
| Use a lowercase UUIDv7 asset identifier or null. | An empty string as an asset ID | Use null. |
| This form exceeds the 2 MB definition limit. Remove unused content and try again. | The definition is larger than 2 MB | Shorten text or remove questions. |

## Publish issues

`issues` appear in `get_form`, in every `save_form_draft` reply and, when publishing fails, in the `publish_form` error:

```json title="A publish error"
{
    "code": "validation_failed",
    "message": "Resolve these issues before publishing.",
    "issues": [
        { "nodeId": "01990000-0000-7000-8000-000000000001", "path": "options", "message": "Add at least two choices.", "severity": "error" }
    ]
}
```

`nodeId` is the ID of a question, rule or calculation, or one of `form`, `theme`, `welcome` and `ending`. `path` says which part of it to fix.

| Issue | Fix |
| --- | --- |
| Add at least one question. | Add a question to `fields`. |
| Write your question. | Give the question a title. |
| Add at least two choices. | Add options to the choice question. |
| Give every choice a label. | Fill in every option's `label`. |
| Add a label for every matrix row. | Add rows to the matrix and label each one. |
| Statements cannot require or collect answers. | Set `required` to false and empty `options` and `rows`. |
| Give the next button a label. | Give `nextButton` a non-blank label, or leave the setting out. |
| Use a welcome button label and a positive response limit. | Fill in `welcome.button`, and set `fillLimit` to null or a number above 0. |
| Use a numeric choice score. | Write `score` as a decimal string, such as "10" or "2.5". |
| Use whole-number scale limits with at most 11 choices. | Use whole numbers for an opinion scale's `min` and `max`, at most 10 apart. |
| Minimum must not exceed maximum. | Swap `min` and `max`. |
| Increase the contrast of form text in Design so everyone can read it. | Use a darker `foreground` or lighter `background`. See [Readable colors](https://trulyform.com/docs/design.md#readable-colors). |
| Increase the contrast of button text in Design so everyone can read it. | Change `buttonText` or `primary` so they contrast at 4.5:1. |
| Choose a question and a target later in the form. | A rule's `sourceId` must come before its `targetId`. See [Logic rules](https://trulyform.com/docs/logic.md#rule-format). |
| Conditions must use an answer available before this rule runs. | Base conditions on the rule's source question or earlier questions. |
| Choose an operator supported by this answer type. | Use an operator the question type supports. See [Conditions](https://trulyform.com/docs/logic.md#conditions). |
| Set a comparison value. | Add a `value` to the condition. |
| This condition references a choice that was removed. Choose an available answer. | Use an option ID that still exists. |
| Name this calculation. | Give the calculation a `name`. |
| This calculation has a missing reference or a circular dependency. | Point every reference at an existing calculation, without loops. See [Calculations](https://trulyform.com/docs/calculations-and-piping.md#expression-nodes). |
| Piped answers must come from an earlier question. | Move the answer token after the question it shows, or pipe a different question. See [Piped answers](https://trulyform.com/docs/calculations-and-piping.md#piped-answers). |

Range issues name the setting, for example "Set maxFiles between 1 and 10." Use a value inside the range in [Settings](https://trulyform.com/docs/question-types.md#settings).

## Error codes

Every tool error carries a `code` and a `message`:

| Code | Meaning | What to do |
| --- | --- | --- |
| `unauthenticated` | The connection is not signed in. | Sign in again from the client. |
| `organization_required` | The account has no organization. | The person joins or creates one in trulyform. |
| `validation_failed` | The input is invalid. `errors` or `issues` say why. | Fix what they name, then retry. |
| `draft_conflict` | The draft changed since the revision you sent, or a `mutation_id` was reused for different content. `revision` holds the current revision. | Read the form again and reapply your change. |
| `not_found` | No such form or response in this organization. | Check the ID with `list_forms` or `list_responses`. |
| `forbidden` | The person is not allowed to do this. | Ask the person. |
| `operation_failed` | Something unexpected went wrong. | Read the form again before retrying a write. |


---

Previous: [Examples](https://trulyform.com/docs/examples.md) · Next: [Publishing and responses](https://trulyform.com/docs/responses.md)

All pages: https://trulyform.com/llms.txt. Connected over MCP? Call read_docs with a page name instead.
