Execution Layer for Odoo
Error codes.
Every failure the Execution Layer returns is a structured, machine-readable code, never prose. This is the reference: the codes grouped by kind, the three that carry a Context Layer hint, and the fields every refusal returns.
The Execution Layer never returns a prose-only error. Every failure is a code an agent can branch on
and a person can act on. The authoritative registry is error-codes.json in the product; the codes
are grouped below by kind.
Request: the call was malformed or ambiguous
| Code | Meaning |
|---|---|
INVALID_ARGUMENT | An argument is missing or the wrong type. |
SCHEMA_VIOLATION | The payload failed the fail-closed schema check. |
FIELD_UNKNOWN | A field named in the call does not exist on the model. |
FIELD_REQUIRED | A field required for AI writes was not provided. |
AMBIGUOUS_TARGET | A human reference matched more than one record; candidates are returned. |
UNRESOLVED_REFERENCE | A human reference matched no record. |
IDEMPOTENCY_KEY_REQUIRED | A write was sent without its idempotency key. |
LIMIT_EXCEEDED | A read or export exceeded the allowed size. |
Policy: the action was not allowed
| Code | Meaning |
|---|---|
MODEL_FORBIDDEN | The model is out of scope for this bundle or connection. |
FIELD_FORBIDDEN | The field is hidden or read-only for MCP. |
PERMISSION_DENIED | Odoo’s own access rules deny it for this user. |
TOOL_NOT_AVAILABLE | The tool is not in this user’s bundle. |
ACTION_NOT_ALLOWLISTED | The execute_action method is not on the allowlist. |
PRECONDITION_FAILED | A declarative business rule was not satisfied. |
APPROVAL_REQUIRED | A risk-classed action was staged; a person must approve it. |
APPROVAL_PENDING | The approval request exists and is awaiting a decision. |
APPROVAL_STALE | The payload changed after approval; the token is invalid. |
APPROVAL_DENIED | A person declined the action. |
EXPORT_DISABLED | Export is off for this bundle. |
RATE_LIMITED | A per-key or per-user rate limit was hit. |
State: the world did not match
| Code | Meaning |
|---|---|
MODEL_NOT_FOUND | The model does not exist. |
TARGET_NOT_FOUND | The target record does not exist. |
CONSTRAINT_VIOLATION | An Odoo constraint rejected the change. |
ASSERTION_FAILED | An only_if assertion did not hold. |
IDEMPOTENCY_CONFLICT | The same key was reused with a different payload. |
PARTIAL_RESULT_REFUSED | A partial result was refused rather than returned as complete. |
BATCH_ITEM_FAILED | An item in an atomic batch failed; the batch rolled back. |
VERIFICATION_FAILED | The post-commit read did not match the declared intent. |
CONTEXT_DEGRADED | The Context Layer is degraded; the pipeline continued without it. |
Server: something went wrong underneath
| Code | Meaning |
|---|---|
TIMEOUT | The operation exceeded its time budget. |
COMMIT_FAILED | The transaction could not commit. |
AUDIT_UNAVAILABLE | In strict mode, the audit write failed, so the call failed. |
INTERNAL | An unexpected internal error. |
The three honest-absence codes
Only three codes ever carry a one-line, factual hint that the Context
Layer could help: AMBIGUOUS_TARGET,
UNRESOLVED_REFERENCE, and FIELD_UNKNOWN. The hint is a status, never
promotional prose injected into a response.
What a refusal returns
Every refusal, including a denial, is audited and returns
a structured object with the machine code, a detail payload (for example the candidate list on
AMBIGUOUS_TARGET), a human sentence, and, where the fix is a policy change, a fix_location
pointing at the bundle or precondition to adjust.
See troubleshooting for what to do about the common ones.