Message Validation
Validation rules applied to the messages collections in Chat Completions and Run requests.
The following rules are enforced on message stacks:
Either directly provided via /chat/completions or built server-side by attaching messages to a thread.
All violations return an HTTP 422 Unprocessable Entity.
Content Rules
These rules apply to the content field of every message type.
| Rule | Details |
|---|---|
| Non-empty | content must not be blank or whitespace-only (applies to user, system, and tool messages) |
| Maximum length | content must not exceed 30,000 characters |
| Valid Unicode | content must be valid UTF-8; broken Unicode characters are rejected |
Assistant message content
assistant messages follow a slightly different rule:
contentmust be non-empty whentool_callsis absent.contentmust be empty whentool_callsis present.
Message List Rules
| Rule | Details |
|---|---|
| Non-empty list | The messages array must contain at least one message |
| Last message | The last message must have role user or tool |
| System message uniqueness | At most one system message is allowed |
| Assistant message ordering | An assistant message must be directly preceded by a user message |
Example — valid message stack
[
{ "role": "system", "content": "You are a helpful HR expert." },
{ "role": "user", "content": "What is BEM?" },
{ "role": "assistant", "content": "BEM stands for ...", "sources": [] },
{ "role": "user", "content": "Can you elaborate?" }
]Example — invalid: assistant without preceding user message
[
{ "role": "system", "content": "You are a helpful HR expert." },
{ "role": "assistant", "content": "BEM stands for ...", "sources": [] }
]Attachment Rules
Attachments can be added to user messages via the attachments field.
Recommended flow: upload the file through the /files endpoints, set user_id
either on the thread (POST /threads) or on the chat-completions request body
(POST /chat/completions), and reference the file by its file_id only — leave
AttachmentCreate.user_id and AttachmentCreate.base_url empty.
| Rule | Details |
|---|---|
| Maximum attachments | 3 attachments per user message. More are rejected with HTTP 422 and error code user_message_too_many_attachments. |
| Uniqueness within a message | The same attachment must not be listed twice on one user message. Rejected with HTTP 422 and error code user_message_not_unique_attachments. |
| Global uniqueness | Each attachment is identified by its file_id, user_id, and base_url. The same attachment must not appear more than once across all messages in a single request. Rejected with HTTP 422 and error code attachment_not_unique. |
user_id optional, do not set | Should be left empty. Provide the user_id once on the thread (POST /threads) or on the chat-completions request body (POST /chat/completions). The AttachmentCreate.user_id override is intended for internal usage only. |
base_url optional, do not set | Defaults to the production file storage URL. The AttachmentCreate.base_url override is intended for internal usage only — e.g. when referencing a file on a non-production environment. A URL outside the allowed set is rejected with HTTP 422 and error code attachment_url_invalid. |
| User ID must be resolvable | If a message carries an attachment, a user_id must be available somewhere — on the thread, on the request body, or (for internal use) on the attachment itself. If none is set the request is rejected with HTTP 422 and error code attachment_requires_user_id. |
| File must exist | The file_id must exist on the file storage service for the resolved user_id. Unknown or already deleted files are rejected with HTTP 404 and error code attachment_document_does_not_exist. |
| File must be accessible | If the resolved user_id is not allowed to read the file, the request is rejected with HTTP 403 and error code attachment_access_forbidden. |
| File must be processed | The file must have reached PROCESSED state on the file storage service before it can be attached. Files that are not finished yet — still uploading, being scanned, or still being processed — return HTTP 409 with error code attachment_document_still_processing; retry once processing completed. Files that finished in any other state return HTTP 400 with error code attachment_document_raised_an_error. |
| File must be clean | A file the storage service flagged as infected is rejected with HTTP 409 and error code conflict. |
| File must contain text | If no text could be extracted from the file (warnings.empty on GET /files/{file_id}/status), the request is rejected with HTTP 422 and error code attachment_is_below_minimum_size. There is no minimum length beyond that. |
| Extracted text must fit the limit | The extracted text must not exceed 60,000 characters, measured on the whole document. Longer documents are rejected with HTTP 422 and error code attachment_exceeds_max_size. Split large documents into several files. |
| Non-persisted files expire | Files uploaded with persist=false (via GET /files/signed-url) are only retained for 24 hours. After that they cannot be used for answer generation. Use persist=true (the default) for files that should remain available longer. |
These checks run when the attachment is added to a message. The file content itself is only loaded
when an answer is generated, so a file that was deleted in the meantime does not fail the request —
it is reported back in meta_data.attachments instead. See
Uploading Files.
Validating an attachment requires calls to the file storage service. If that service is
unreachable, the request fails with HTTP 502 and error code attachment_document_not_reachable.
Unlike the rules above this is transient and not caused by your request — retry it.
Tool Message Rules
Tool messages (role: "tool") return tool call results to the assistant.
| Rule | Details |
|---|---|
| Must follow an assistant message | A tool message must be preceded by an assistant message that requested the corresponding tool call |
| All requested tool calls must be answered | If an assistant message requests multiple tool calls, all corresponding tool messages must immediately follow it |
Unique tool_call_id | Each tool_call_id must be unique across the entire messages array |
Example — valid tool message sequence
[
{ "role": "user", "content": "Search for BEM regulations." },
{ "role": "assistant", "content": "", "sources": [], "tool_calls": [
{ "id": "call_1", "type": "function", "function": { "name": "search", "arguments": "{}" } },
{ "id": "call_2", "type": "function", "function": { "name": "fetch", "arguments": "{}" } }
]},
{ "role": "tool", "content": "Search result...", "tool_call_id": "call_1" },
{ "role": "tool", "content": "Fetch result...", "tool_call_id": "call_2" }
]Example — invalid: missing tool message for call_2
[
{ "role": "user", "content": "Search for BEM regulations." },
{ "role": "assistant", "content": "", "sources": [], "tool_calls": [
{ "id": "call_1", "type": "function", "function": { "name": "search", "arguments": "{}" } },
{ "id": "call_2", "type": "function", "function": { "name": "fetch", "arguments": "{}" } }
]},
{ "role": "tool", "content": "Search result...", "tool_call_id": "call_1" }
]Tool Choice Rules
When using the tools and tool_choice fields on the request:
| Rule | Details |
|---|---|
tool_choice requires tools | tool_choice cannot be set if tools is absent or empty |
| Named tool choice must be valid | If tool_choice specifies a function by name, that function must be present in the tools list |
Defaults:
- If
toolsis provided buttool_choiceis omitted, it defaults to"auto". - If neither is provided,
tool_choicedefaults to"none".