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 |
| 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 |
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. |
| 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 be processed | The file must have reached PROCESSED state on the file storage service before it can be attached. Files still being processed return HTTP 409 with error code attachment_document_still_processing. Files in any other non-processed state return HTTP 400 with error code attachment_document_raised_an_error. |
| 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. |
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".