Platform
Error codes
The unified JSON error format of the gate and DocBuilder, the full error code table with remedies, and when to retry.
On this page
Unified error format#
Every error from the gate (/o3o/*) and DocBuilder (/v1/*) has the same JSON body. Every response carries an X-O3O-Request-Id header equal to request_id.
{
"error": {
"code": "unsupported_format",
"message": "Không chuyển được từ docx sang xlsx.",
"detail": {
"from": "docx",
"to": "xlsx"
},
"request_id": "req_0123456789abcdef"
}
}Fields of the <code>error</code> object
codestringrequiredStable machine-readable string. Branch on this field.messagestringrequiredVietnamese sentence for people; wording may change between versions, do not parse it.detailobjectrequiredDefault:{}Machine-readable details, possibly empty; never contains document content.request_idstringrequiredreq_+ 16 hex characters; include it when reporting a problem.
- Branch on
code, not onmessage. - For an unknown
code(later versions may add codes): fall back to the HTTP status class. - Log
request_idso it can be matched against the O3O server logs. - Two exceptions do not use this JSON shape: the connection-limit HTML page (HTTP 429) shown to users, and
/o3o/auth, which only the internal proxy calls.
DocBuilder error codes#
| HTTP | code | When | What to do |
|---|---|---|---|
| 400 | bad_request | Missing parameter, broken JSON, wrong options type, neither file nor url. | Fix the request using detail.errors (path, message). Do not resend it unchanged. |
| 401 | unauthorized | Missing or wrong key. detail.reason: missing, invalid, no_keys_configured. | Check the Authorization header and the key list. no_keys_configured: no key has been configured yet. |
| 401 | token_expired | The JWT has expired. | Sign a new JWT and resend. |
| 403 | forbidden_feature | Endpoint or option not included in the current edition. detail.feature, detail.edition. | Use another approach available in the edition, or move to the enterprise edition. Do not retry. |
| 404 | not_found | No job or file with that id. | Check the job_… or file_… id. |
| 410 | gone | The job or file is past its retention time. | Send the original request again; next time download before result_ttl_minutes. |
| 413 | file_too_large | Input or output file exceeds max_file_mb. detail.limit_mb. | Shrink or split the file; see Plan limits. |
| 415 | unsupported_format | Unknown source format, or an unsupported source–target pair. | Check GET /v1/formats; conversion only works within one document family. Pass from when the file name has no extension. |
| 422 | script_invalid | The script fails JSON Schema validation. detail.errors[].path is a JSON Pointer. | Fix the reported locations; validate beforehand with o3oscript-v1.schema.json. |
| 422 | script_too_large | Script units exceed max_script_units. detail.units, detail.limit. | Split the script into several smaller documents. |
| 422 | script_error | Valid script that failed at run time. detail.path points at the offending element. | Fix the element at detail.path. |
| 422 | template_error | Unbalanced loop tags, or missing fields with missing = "error". detail.fields. | Balance the {{#…}}/{{/…}} tags within one row; add the data or change options.missing. |
| 422 | macro_not_allowed | The template sent to POST /v1/template/render contains macros: an OOXML file with a vbaProject.bin part or a macroEnabled content type, or an ODF file with a Basic/ or Scripts/ folder. detail.reason, detail.part; returned immediately even with async = true. | Save the template again without macros (for example docm as docx) and resend it. Conversion and extraction still accept files with macros because they load files with macro execution off. |
| 422 | corrupt_source | The source file cannot be opened. | Check the file with an office application; do not retry unchanged. |
| 422 | password_required | Password-protected file with a missing or wrong password. | Pass options.password (conversion) or password (extraction). |
| 422 | url_not_allowed | The URL violates the SSRF rules. | Use a public http/https URL, or upload the file as multipart. Internal hosts must be added to O3O_FETCH_ALLOW_HOSTS by the administrator. |
| 422 | download_failed | The source URL could not be fetched (network error, non-200 status, timeout). | Check the URL from the O3O server's side; a later retry may succeed. |
| 429 | rate_limited | Rate limit exceeded. detail.window: minute or day. | Wait Retry-After seconds, then retry. See Plan limits. |
| 500 | internal | Unexpected error. | Retry once; if it repeats, send the request_id to the administrator. |
| 503 | queue_full | The queue is full (max_queued_jobs). | Wait for Retry-After; send fewer requests in parallel. |
| 503 | pool_unavailable | No worker process is alive. | Retry later; the administrator should check the o3o-docbuilder logs. |
| 504 | timeout | Synchronous mode exceeded sync_timeout_seconds, or the job exceeded job_timeout_seconds. | Heavy file: use async = true. Job timeout: split the work. |
Gate and embedding error codes#
The gate shares DocBuilder's three generic codes with the same meaning: not_found (404), bad_request (400, or the original HTTP status such as 405 for a wrong method) and internal (500). The internal WOPI host additionally returns not_implemented (501) for WOPI operations not available in v1. The other codes in the table are specific to the embedding layer.
| HTTP | code | When | What to do |
|---|---|---|---|
| 400 | bad_request | Shared code, same meaning as in DocBuilder: invalid parameters or body; a method not supported on that path (the HTTP status is kept, for example 405, with detail.method); an empty PutFile body. | Fix the request; do not resend it unchanged. |
| 400 | invalid_config | Missing or invalid config field. detail.errors = [{path, message}]. | Fix the config at each path. |
| 401 | token_required | The server requires a JWT but the config has none. | Sign the config on your server, see Authentication. |
| 401 | invalid_token | Bad signature, not HS256, missing exp, or exp more than 24 hours away. | Sign again following the rules. |
| 401 | token_expired | Past exp. | Sign a new token. |
| 401 | embed_auth_not_configured | The server has no key and unsigned mode is off. | The administrator sets O3O_EMBED_JWT_SECRET. |
| 401 | invalid_session_token | Authorization does not match the session. | Use the right session's access_token (api.js does this for you). |
| 403 | origin_not_allowed | Origin is not listed in O3O_EMBED_ALLOWED_ORIGINS. | The administrator adds the page origin to the list. |
| 403 | invalid_signature | The download URL signature is wrong. | Use the URL exactly as received from the callback or onSaved. |
| 404 | not_found | Shared code, same meaning as in DocBuilder: unknown path, the session or document does not exist, or embedding is disabled. | Check the path and the session id; check O3O_EMBED_ENABLED. |
| 410 | link_expired | The download URL has expired. | Get a fresh URL from GET /o3o/embed/session/{session_id} while the session exists. |
| 410 | version_gone | The version was cleaned up (the gate keeps the latest 5 plus the original). | Download a newer version. |
| 413 | file_too_large | Exceeds O3O_EMBED_MAX_FILE_MB. | Reduce the file size or raise the limit on the server. |
| 415 | unsupported_file_type | fileType is not in the accepted list. | Use one of: docx, doc, odt, rtf, txt, xlsx, xls, ods, csv, pptx, ppt, odp. |
| 422 | url_not_allowed | document.url or callbackUrl violates the SSRF rules. | Use a public URL, or have the host added to O3O_FETCH_ALLOW_HOSTS. |
| 422 | download_failed | document.url could not be fetched. | The URL must answer 200 to the O3O server. |
| 500 | internal | Shared code, same meaning as in DocBuilder: an unexpected error inside the gate. | Retry once; if it repeats, send the request_id to the administrator. |
| 501 | not_implemented | A WOPI operation not available in v1 (for example X-WOPI-Override values such as PUT_RELATIVE or RENAME_FILE). Only the editing server sees this code, because the proxy does not expose /o3o/wopi/. | Nothing to handle on the integration side; the matching feature is coming soon. |
| 503 | editor_unavailable | The editing server's discovery could not be read. | Retry after a few seconds; check upstream.coolwsd in /o3o/status. |
The connection_limit_reached code (403) only travels from the gate to the internal proxy. Users never get this JSON: through Nextcloud they see the "connection limit reached" page (HTTP 429), and embedded sessions open read-only with an onLimitReached event. See How connections are counted.
Browser-only codes#
api.js also emits the codes below through the onError event as {code, message, detail}.
code | When | What to do |
|---|---|---|
network_error | The O3O server cannot be reached. | Check the api.js address, CORS and the network; retry. |
load_failed | The editor frame reported a load failure, or no document after 120 seconds. | Check the source file and O3O_ONLINE_FRAME_ANCESTORS for cross-origin pages. |
save_failed | The editing server reported a failed save; detail holds the raw message. | Call save() again; the document is still in the session. |
save_timeout | save() took more than 30 seconds. | Retry; check the network connection. |
When to retry#
| Group | Codes | Action |
|---|---|---|
| Transient | 429 rate_limited, 503 queue_full, 503 pool_unavailable, 503 editor_unavailable, network_error | Retry after Retry-After (when present) or with growing back-off. |
| Possibly transient | 500 internal, 504 timeout, 422 download_failed | Retry once; for timeout switch to async = true. |
| Fix the request | 400, 413, 415, 422 script_*, 422 template_error, 422 macro_not_allowed, 422 url_not_allowed, 422 password_required, 422 corrupt_source | Never resend unchanged. |
| Fix credentials or edition | 401, 403 | Fix the key, token, origin or edition. |
| Nothing to retry | 404, 410, 501 not_implemented | Create the job again or obtain a new URL; 501 means the feature is not available yet. |