Every stable error code the API can return, and what each one means.
When a request fails, the response body carries a stable machine-readable code alongside
the human-readable message:
{ "error": { "error": "App database is not provisioned yet", "code": "APP_NOT_PROVISIONED", "details": { "appUid": "..." } }}
Branch on error.code, never on error.error. The code is the contract and will not
change; the message is prose and may be reworded at any time. details carries whatever
context is useful for that specific failure.
The Status column is the HTTP status the code is actually thrown with, verified against
the real throw sites by a test in the backend suite rather than maintained by hand.
Not every failure carries a code. A generic policy denial returns 403 with code: null,
so treat error.code as present-or-absent rather than assuming one is always there.
A field the data API does not take: a row's internal id, its uid (minted by the platform), a scope or actor column, or a raw foreign-key column. The message names the field to use instead.
ROW_REFERENCE_NOT_FOUND
400
A <column>_uid reference names no row in the table it points at. The message names the column.
ROW_EDGE_COLLISION
409
The table cannot be served: a foreign-key column's uid name (<column>_uid) is also the name of a real column. Rename one of them.
ROW_ORG_REQUIRED
400
The table's rows belong to an organization: name the app organization a new row belongs to (orgUid in the console and agent tools, the row's org_uid in a handler acting for the app's owner). An app with no organizations yet cannot take rows in such a table.
ROW_ORG_NOT_FOUND
400
orgUid (or a row's org_uid) names no organization in the target app that the caller reaches.
PAGE_CURSOR_INVALID
400
A page read with after was given a cursor this API did not issue for that table (changed, from another table, or sealed under a retired secret), or a sort or offset, which keyset paging does not take.
PAGE_CURSOR_UNAVAILABLE
500
The server has no secret to seal page cursors with (ACCESS_TOKEN_SECRET is not set).
AGENT_ORG_UNRESOLVED
403
The agent belongs to no organization of the app it is acting on, and is not the app owner's.
APP_ORG_UNBOUND
500
Platform code tried to reach an app's data without saying which app organization it acts in.
ORG_REACH_OWNER_ONLY
403
Only the app's owner, in the console, can change what its agents, jobs and keys may reach.
ORG_REACH_NOT_APPLICABLE
409
This agent, job or key belongs to one of the app's organizations and acts only there; org reach is for the owner's own.
STORAGE_SHARED_READ_ONLY
403
This was shared with you by the app's owner: you can open and download it, and add your own files beside it, but not change it or add folders inside it.
STORAGE_SHARING_OWNER_ONLY
403
Only the app's owner decides what its folders and files are shared with.
STORAGE_SHARING_NOT_APPLICABLE
409
This belongs to one of the app's organizations; sharing is for the owner's own folders and files.
STORAGE_OUT_OF_REACH
403
That reaches organizations outside the caller's org reach.
The target plan does not fit the organization's current usage. Raised in EITHER direction: a plan's rank decides how it is billed, not which way each individual limit moved, so an upgrade that tightens one limit is refused too. details.blockers lists every resource that is over, with the usage and the target limit, so the customer can free them all at once instead of being refused repeatedly.
PLAN_CHANGE_ALREADY_SCHEDULED
409
A plan change is already scheduled for the end of the current period. Cancel it before scheduling another — a second would orphan the first schedule in Stripe.
NO_PENDING_PLAN_CHANGE
409
There is no scheduled plan change to cancel.
PLAN_PAYMENT_FAILED
402
The card could not be charged for an upgrade, so the plan was NOT changed — the whole change is rolled back rather than granted unpaid. Update the payment method and retry.
The account is read-only because a payment failed. Reads still work; bring the subscription current to write again. details: { state, reason: 'lifecycle', stateReason }
ACCOUNT_PAUSED
423
The app is paused — for inactivity (restore it from the console) or for non-payment (bring the subscription current). details.stateReason says which. details: { state, reason: 'lifecycle', stateReason }
APP_WAKING
503
The app was asleep after a period of no traffic and this request started waking it. Retry after the Retry-After interval. details: { retryAfterSeconds, state, stateReason }
APP_RESTORING
503
The app is being restored from its archive and cannot serve yet. Retry after the Retry-After interval. details: { appUid, retryAfterSeconds }
ACCOUNT_TORN_DOWN
423
The app's live data was removed after a long pause. It can still be restored from its archive until the archive expires. details: { state, reason: 'lifecycle', stateReason }
A branding field is not usable: a color that is not #rrggbb, a URL that is not https, an address that is not an email address, or text past its length. The message names the field.
BRANDING_ASSET_NOT_FOUND
404
No file by that uid among the app owner's own files, so it cannot be the logo or icon.
BRANDING_ASSET_NOT_PUBLIC
400
The file is in a private storage, so an email cannot show it. Move it to a public storage, or make its storage public.
SMS_NOT_CONFIGURED
500
This deployment has no SMS sender configured. An operator must configure one.
SMS_CHANNEL_UNSUPPORTED
501
The configured SMS provider cannot send this kind of message. Switch to the Programmable Messaging adapter.
SMS_SUPPRESSED
409
That number has opted out of messages from this sender and cannot be texted.
ARBITER_NO_LLM
409
No AI provider is connected for this organization. Connect one in Integrations.
AI_NO_LLM
409
No AI provider is connected for this organization. Connect one in Integrations.
AI_MODEL_UNAVAILABLE
409
The configured AI model is no longer available from this provider. Choose a replacement to resume.
LLM_MODEL_NOT_APPROVED
400
That AI model is not approved for this provider. Choose one from the model list.
AI_TIER_INVALID
400
sdk.ai takes a tier ("fast" or "default"), not a model. Set the models on your AI connection.
This account cannot be a member of that organization.
STAFF_CAPABILITY_NOT_GRANTABLE
403
Those actions belong to Authoric staff and can only be granted inside the staff organization.
ORG_NOT_BILLED
404
This organization is not billed; it has no plan or usage to show.
STAFF_ONLY
403
This action is for Authoric staff only.
PLATFORM_TARGET_STAFF_ONLY
403
The platform database is not an app target; name one of your organization's apps as targetAppUid.
ACT_AS_INVALID
400
That act-as request names something we cannot act on.
ACT_AS_REASON_REQUIRED
400
Say why you need access to this organization; the reason is recorded and shown to the customer.
ACT_AS_NOT_HUMAN
403
Only a person can act as an organization.
ACT_AS_STAFF_ORG
400
The staff organization is not something to act as.
ACT_AS_NOT_A_MEMBERSHIP
409
You are acting as this organization, not a member of it — switch back to stop.
MEMBER_TOKEN_SELF_ONLY
403
An approver token can only be minted for your own account.
MEMBER_TOKEN_STAFF_ORG
403
Approver tokens cannot be minted in the staff organization; staff act through sessions.
AI_MODEL_INVALID
400
The AI model catalog change is invalid; the message says which field and why.
AI_MODEL_NOT_FOUND
404
No such model in the AI model catalog.
AI_MODEL_EXISTS
409
That provider and model id are already in the AI model catalog.
AI_MODEL_IN_USE
409
The model is still in use. It fills a platform role (reassign the role first), or AI connections chose it and the change needs confirming. details lists what points at it.
AI_MODEL_NOT_VERIFIED
409
Verify the model before approving it: a verification run proves it works through the platform's adapter.
AI_MODEL_UNPRICED
409
The model has no price, so its spend would not count toward AI spend caps. Add a price, or confirm running it unpriced.
LLM_CONNECTION_NOT_FOUND
404
No such AI connection in the active organization.
AI_SOURCE_REDACTED
400
A derived column cannot read from a redacted column. Remove it from the source list or un-redact it.
AI_DERIVED_COLUMN_INVALID
400
The derived-column configuration is invalid.
AI_EMBEDDINGS_UNSUPPORTED
409
The connected AI provider does not offer embeddings. Semantic search needs a provider that does.
APPROVAL_SELF_APPROVAL
403
The caller raised this approval request and cannot approve it themselves.
HANDLER_RUN_NO_PRINCIPAL
409
Run live executes as a principal of the app, and a console session is not one. Use the dry run, or call the endpoint as one of the app's users.
APPROVAL_CONSUMED
409
This approval was already used by another run of the same work. An approval runs its work once.
APPROVAL_ORG_REQUIRED
403
Approvals belong to an organization, and this caller has no active organization.
STEP_ALREADY_DECIDED
409
This approval step was already decided by another surface. Refresh to see the current state.
API_KEY_INVALID
401
The API key is missing, malformed, unknown, revoked, or expired. Returned identically for all five.
You lack an SDK capability the handler's code uses. Request the capability to author it. details: { missing: [{ capability, label, risk, methods }], actor } — the capabilities required but not held.
DDL_NOT_DRY_RUNNABLE
422
A dry-run tried to execute schema-changing SQL. DDL cannot be rolled back, so it is never dry-runnable.
SDK_CAP_NOT_IN_MANIFEST
403
The handler called an SDK power absent from the manifest frozen at publish time. Republish to refresh it.
SDK_UNKNOWN_METHOD
400
The handler called an SDK method that does not exist. Check the spelling against the editor's autocomplete — the contract d.ts is the full surface.
LINK_TOKEN_INVALID
404
The link is invalid, expired, already used, revoked, or minted for a different purpose. Returned identically for all five.
The chart spec failed structural validation. details: { issues: string[] } — the specific validation failures.
CHART_SNAPSHOT_TOO_LARGE
413
The chart is well-formed but its frozen data snapshot exceeds the storage/serve cap. details: { maxRows, maxBytes } — the caps that were exceeded.
CHART_NOT_FOUND
404
No such chart within your scope. Returned identically for a chart that does not exist and one owned by another organization.
CROSS_PLANE_FORBIDDEN
403
An app-plane caller tried to address another plane. An app may only touch its own database.
GRANT_EXCEEDS_HOLDER
403
You tried to grant capabilities you do not hold yourself. The response names which ones; ask someone who holds them, or have them granted to you first.
ROLE_PROTECTED
400
Built-in roles cannot be deleted. Create a custom role instead, or unbind this one from whoever holds it.
No such agent run within your scope. Returned identically for a run that does not exist and one belonging to another organization.
AGENT_RUN_CONCURRENCY_LIMIT
429
The organization already has its maximum number of agent runs in flight. Retry after the interval in the Retry-After header. details: { limit, retryAfterSeconds } — the ceiling and when to retry.
AGENT_RUN_NOT_ACTIVE
409
That run has finished, so a message can no longer be sent to it. Send it to the conversation instead to start new work.
AGENT_SESSION_BUSY
409
This conversation already has a run in progress that you did not start. Wait for it to finish, or use a new conversation.
STALE_APPROVAL
409
The rows this approved step would change have changed since it was approved, so it was not run. Read them again and propose it afresh.
CHANGE_SET_NOT_FOUND
404
No recorded change with that handle in your runs. It may belong to someone else, or be older than the retention window.