Errors & Recovery¶
Errors are designed to be agent-actionable: every failure carries a kind
plus, where relevant, the offending field, a recovery hint, and structured
extra data. Agents shouldn't need to parse prose to recover — and neither
should your automation.
Error kinds¶
| Kind | Meaning | Right response |
|---|---|---|
VALIDATION |
A field failed a format/range rule | Fix the named field, retry |
MISSING_FIELD |
A required field/parameter is absent | Supply it (e.g. build-context needs site or region) |
REF_NOT_FOUND |
An id doesn't exist or isn't in your tenant | Don't retry the id — look the entity up by name. Foreign and nonexistent ids are deliberately indistinguishable |
CONFLICT_STATE |
The load's current status doesn't allow this action | Read extra.allowed_transitions and relay them; don't retry the same transition |
OUT_OF_RANGE |
A numeric bound was violated (quantity, limit, …) | Adjust the value |
AUTH_REQUIRED |
No valid credential reached the backend | Re-authenticate / check the key |
AUTH_AMBIGUOUS |
Both an OAuth bearer and an API key were sent | Send exactly one credential |
Validation errors vs. warnings¶
validate_load separates the two explicitly:
errors[]block creation. Each entry names the field path (e.g.assignments[0].line_items[0].tank_id) and why it failed.warnings[]don't block — they flag judgment calls for a dispatcher:window_short,quantity_unusual,tank_overfill, driver PTO conflicts, off-allowlist equipment, unusual tank↔product pairings. Creating over warnings requires explicit acknowledgment.
Gate errors (not backend errors)¶
These come from the MCP server's own write gates, before any backend call:
| Message contains | What happened | Recovery |
|---|---|---|
| "unknown or expired" | The validation/confirm token aged out (15 min) or the server restarted | Re-run validate_load (or the preview call) and use the fresh token |
| "payload changed since it was validated" | The draft was mutated after validation | Re-validate the current payload |
| "declined by the user" | The human said no at the confirmation prompt | Adjust the plan per their feedback; don't retry unchanged |
| "warning(s). Surface them" | Warnings weren't acknowledged | Show the user, then pass acknowledge_warnings=true |
| "not enabled for this account" | The server is in allowlist mode and your organization isn't enabled | Contact FolioX |
Partial failure¶
Bulk creates return per-item results. success_count < total_count with
entries in errors[] means some loads were created and some weren't — the
response is explicit about which, and agents are instructed to report exactly
that rather than a blanket success.
Timeouts & retries¶
- Reads are safe to retry as-is.
create_loadretries are idempotent (the idempotency key replays the original result — it will not double-create).- Mutation executes are not blindly retryable: re-run the preview phase to get a fresh token against current state.