Errors

Every Appstrate API error is an RFC 9457 problem document with a stable machine-readable code and a request id.

Errors are returned as application/problem+json per RFC 9457, with a few Stripe-style extension members. Branch on the HTTP status and on code. Never branch on title or detail, which are written for humans and can change.

Format

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Request-Id: req_6f1c9a52-7b0e-4b7d-9c2e-0d3a8a1f5e44
{
  "type": "https://docs.appstrate.dev/errors/validation-failed",
  "title": "Validation Failed",
  "status": 400,
  "detail": "name: Too small: expected string to have >=1 characters (+1 more)",
  "instance": "urn:appstrate:request:req_6f1c9a52-7b0e-4b7d-9c2e-0d3a8a1f5e44",
  "code": "validation_failed",
  "request_id": "req_6f1c9a52-7b0e-4b7d-9c2e-0d3a8a1f5e44",
  "errors": [
    {
      "field": "name",
      "code": "out_of_range",
      "message": "Too small: expected string to have >=1 characters"
    },
    {
      "field": "scopes",
      "code": "invalid_type",
      "message": "Invalid input: expected array, received string"
    }
  ]
}
FieldAlwaysDescription
typeYesAn identifier URI for the error class: https://docs.appstrate.dev/errors/ plus the code with underscores turned into hyphens. An identifier, not a link; see Type URIs to find its row
titleYesShort human-readable summary
statusYesThe HTTP status, repeated in the body
detailYesHuman-readable explanation of this occurrence
instanceYesurn:appstrate:request: followed by the request id
codeYesStable machine-readable code in snake_case
request_idYesThe request id, also sent in the Request-Id response header
paramNoThe single request parameter at fault, such as a header name or a query field
retry_afterNoSeconds to wait before retrying. Mirrored in the Retry-After header
errorsNoField-level errors, one entry per offending field, on validation_failed and on some readiness checks

Some errors carry additional members the client is expected to act on. For example, a run launch blocked by a missing connection returns the connections to choose from or to reconnect (candidate_connections, required_scopes, and similar) so a UI can resolve it without a second round trip. These members are documented on the operation that emits them.

Property names in problem documents are snake_case, like the rest of the wire format. A few identifiers keep their camelCase spelling (id, userId, createdAt, and similar); see the OpenAPI schemas.

Request ids

Every response, success or error, carries a Request-Id header of the form req_<uuid>. Quote it in support requests and log it on your side: the platform's logs and audit records carry the same id. If you send a valid W3C traceparent header, it is echoed back and the trace id is attached to the server logs for that request.

Validation errors

A request body that fails validation returns 400 with code: "validation_failed" and one errors[] entry per offending field, all in the same response. Bodies are strict: an unknown field is an error, not silently dropped, and a malformed JSON body is a 400 rather than a 500.

Field codeMeaning
requiredA required field is missing
invalid_typeWrong type
invalid_formatWrong format: not an email, not a URL, not an ISO date
out_of_rangeToo short, too long, too small, or too large
unknown_fieldThe body contains a field the schema does not accept
invalid_valueNot one of the allowed values, or a custom rule failed
invalid_union, invalid_key, invalid_elementA nested union, map key, or array element failed
invalid_manifestA field of a package manifest is invalid
invalid_requestAny other validation failure

A missing field is required whatever its type, enum and union fields included. Three cases keep the code of the check that failed: a missing discriminator of a discriminated union (invalid_union), a field checked by a rule on the whole object (invalid_value), and a missing numeric field the schema coerces, such as size on POST /api/uploads (invalid_type).

Some operations put a field code of their own in errors[], such as invalid_input (a schedule's input), file_uri_in_prompt, no_tools_selected, scope_not_in_catalog and unrenderable_authorized_uri. Treat a field code you do not recognise like invalid_request.

field is a path: scopes, metadata.plan, or items[0].name for array elements.

A single bad query parameter or header is reported with code: "invalid_request" and a param instead of an errors[] array.

Type URIs

The type member is built from code: https://docs.appstrate.dev/errors/ followed by the code with every underscore replaced by a hyphen.

code:  validation_failed
type:  https://docs.appstrate.dev/errors/validation-failed

The URI identifies the error class. It is not a link you can rely on: docs.appstrate.dev does not serve it today. To find a code on this page, use its anchor: every row of the catalogues below carries code- followed by the code in lower case with hyphens (#code-validation-failed).

Lower-case before you look up

The substitution replaces underscores and nothing else. It does not lower-case, so the few upper-case codes produce a mixed-case URI:

code:  INVALID_URL
type:  https://docs.appstrate.dev/errors/INVALID-URL

Its row is #code-invalid-url: lower-case the last segment of the type, or the code with its underscores turned into hyphens.

Two catalogues

Two layers raise coded errors: the platform HTTP API (Error code catalog) and the AFPS runtime and CLI (AFPS runtime error codes). Only the API puts a type in a response; the runtime emits none. The runtime's rows take the anchor prefix code-afps-.

They are kept apart because the two catalogues collide. The API's integrity_mismatch (a version already published with different content) and the runtime's INTEGRITY_MISMATCH (stored bytes no longer matching their recorded hash) are unrelated failures. Under one anchor, one of the two readers would get the wrong explanation.

Error code catalog

These are the codes the platform HTTP API returns. The afps CLI and the embeddable runtime raise a separate set, catalogued under AFPS runtime error codes.

Four codes can come back from almost any endpoint: unauthorized, forbidden, internal_error and rate_limited. validation_failed, invalid_request and not_found cover most of the rest. Everything else is specific to a domain.

This list is maintained by hand and is not closed. It is checked against the code, but a release can add a code before this page lists it, optional modules (a billing module, for example) contribute codes of their own, and a self-hosted instance can load modules this page knows nothing about. Branch on status first and on code second, and treat a code you do not recognise as its status class rather than failing hard. Each operation in the OpenAPI document lists the responses it can return.

Codes marked billing module appear only when a billing module such as @appstrate/module-ee is loaded. Codes marked sidecar come from the /internal routes that the run sidecar calls, not from the public API.

CodeStatusMeaning
agent_in_use409The agent still has runs in progress, so it cannot be deleted, published, or have a version restored or deleted yet
agent_not_active_in_space404The agent is placed in the space but switched off there. Activate it with POST /api/spaces/{spaceId}/packages, or use another space
agent_not_found404No agent with that package id is reachable in this space
archive_required415An MCP-server package must be uploaded as a multipart .afps or .zip archive
authentication_required401Accepting an invitation needs a signed-in user
bad_gateway502An upstream dependency of the platform failed or returned an unusable response
blocked_target403Credential or model proxy: the upstream resolves to a blocked (private or reserved) address. A Proxy-Status header says the proxy refused, not the upstream
blocked_url400A URL you submitted (a webhook endpoint, a proxy URL) resolves to a private or reserved network address
bootstrap_org_failed500First-run bootstrap created the account but not its organization. The instance is in a partial state
bootstrap_owner_email_mismatch403The instance names its owner in AUTH_BOOTSTRAP_OWNER_EMAIL. Claim it with that address
bootstrap_redeem_in_progress409Another bootstrap redemption is already running on this instance
bootstrap_signup_failed500Signup failed while redeeming the bootstrap token
bootstrap_signup_rejected400 / 422The auth layer rejected the bootstrap signup. 422 when the password policy refused the password
bootstrap_token_invalid401The bootstrap token does not match the instance's
bootstrap_token_unavailable410No bootstrap token is redeemable: none is configured, or it was already redeemed
bootstrap_user_exists409An account already exists for that email; the bootstrap token only creates a new one
bootstrap_user_lookup_failed500The bootstrap signup appeared to succeed but the user could not be found
bundle_conflict409An imported bundle contains a package owned by another organization, or one deleted during the import
bundle_integrity_mismatch500A stored package artifact no longer matches the integrity hash recorded when it was published. Republish the package. This is the HTTP face of the runtime's INTEGRITY_MISMATCH
bundle_invalid422A stored bundle cannot be assembled into a runnable package, or an MCP-server package has no activatable or executable version. detail names the runtime code
bundle_signature_invalid422A stored bundle failed the AFPS signature policy (AFPS_SIGNATURE_POLICY). Republish it signed by a trusted key
chat_capacity429The chat engine is at its session cap. Retry after retry_after seconds
chat_enforced_not_skill400Only a skill can be enforced in a space's chat
checksum_mismatch400The SHA-256 you declared does not match the bytes the server received
conflict409The package's files were modified concurrently. Reload and retry
connect_run_no_refresh409Sidecar. A connect run holds no stored credential to refresh
connect_unavailable503This connection method is not available on this deployment
connection_blocked_by_admin403An administrator disabled personal connections to this integration. Use the shared connection
connection_label_taken409Another connection of this integration already has that label
connection_not_in_org_default400The connection named in X-Connection-Id is outside the organization default enforced for that integration
connection_not_in_run400The connection named is not bound to that integration by this run
connection_owner_without_access409The connection's owner no longer has access to the space, so it cannot be shared there
connection_pinned409The connection is pinned to agents by an admin, or named by an organization default. Remove it from there first
content_entry_immovable400Editing package files: a required content file cannot be removed, renamed or emptied
credential_exfiltration_refused403Credential proxy: the call carries a credential but no authorized_uris entry binds the target's host: the list leaves the host to the caller, or a host wildcard is not under a registrable domain of its own, or the target's registrable domain lies outside the wildcard's literal part. The message names the host to list
credential_in_use409Models still reference this model-provider credential. Detach them first
credential_not_found404Credential proxy: no usable credential was found for the integration
credential_unusable502Credential proxy: the connection's credential is unusable once substituted or injected. Reconnect it
default_space_not_deletable409The default space cannot be deleted
delete_failed400The organization could not be deleted, for example while runs are in progress. detail names the cause
dependency_unresolved422A declared dependency does not resolve to a usable published version. Publish it, fix the pin, or pass dependency_overrides. This is the HTTP face of the runtime's DEPENDENCY_UNRESOLVED
DOWNLOAD_FAILED400GitHub import: a file of the repository could not be downloaded
draft_not_writable403Running a draft requires write authority on it. Run the latest published version instead
draft_overwrite409The import would overwrite unpublished draft changes
draft_with_spec400stage: "draft" cannot be combined with a version spec: drafts have no published id
draft_with_version400?source=draft cannot be combined with ?version: drafts have no published id
duplicate_file_name400Two input files of a run resolve to the same workspace file name
email_mismatch403The invitation was issued to another email address than the signed-in user's
EMPTY_PATH400GitHub import: no files were found at that path
empty_prompt400The agent's prompt is empty, so it cannot run
end_user_connection_not_shareable409An end-user's connection cannot be shared with the space (shared_with_org: true)
enforced_skills_budget409The skills enforced in a space's chat would exceed the chat's content budget
enforced_skills_limit409A space already enforces the maximum number of chat skills
enforced_skills_unavailable503The skills a space requires in its conversations could not be loaded
external_id_taken409Another end-user in this space already uses that externalId
file_count_exceeded413A run would reference or publish more files than RUN_MAX_FILES allows
file_in_use409The file is referenced by one or more runs and cannot be deleted
file_too_large, FILE_TOO_LARGE400 / 413A file is over its size limit. 413 when editing package files; 400 from a package ZIP import, and in upper case from a GitHub import
file_unavailable409One or more input files were deleted before the run could be created
forbidden403The credential is valid but lacks the permission, role or ownership the operation needs
GITHUB_ERROR400GitHub import: GitHub answered with an error
header_not_allowed400A header was sent under an authentication method that cannot honor it (Appstrate-User outside API-key auth)
home_move_into_personal_space409A package cannot be re-homed into a personal space. Fork it instead
idempotency_conflict422The Idempotency-Key was already used with a different method, URL or body
idempotency_in_progress409A request with the same Idempotency-Key is still being processed. Retry shortly
idempotency_not_supported400Idempotency-Key was sent with an unsafe method to an operation that does not honor it. It is refused, not ignored
identity_mismatch409The reconnected account differs from the one the connection is linked to
in_use409Other packages still depend on the package, so it cannot be deleted
injected_draft_changed409Chat: the skill draft changed since the conversation turn injected it
integration_auth_undeclared409The integration version the run pins does not declare the auth its connection was created against
integrity_mismatch409The version already exists with different content. Bump the version, or force-replace. Unrelated to the runtime's INTEGRITY_MISMATCH
internal_error500Unexpected server failure. The body never leaks internals
invalid_api_version400The Appstrate-Version header is not a YYYY-MM-DD date
invalid_bundle400Editing package files: the content is not valid UTF-8 text, or the files fail package validation
invalid_config400The integration manifest's identity_claims holds a path that is not valid JSONPath
invalid_content400Package ZIP import: a companion file is invalid, such as a SKILL.md without a frontmatter name
invalid_cron_expression400The schedule's cron expression is invalid
invalid_draft_manifest400The draft manifest is invalid or lacks a scoped name and version
invalid_end_user403The Appstrate-User end-user does not exist, or belongs to another space
invalid_end_user_id400The Appstrate-User header is not an eu_ end-user id
invalid_idempotency_key400The Idempotency-Key header exceeds the maximum length
invalid_inline_manifest400The manifest sent with an inline run is not a valid AFPS agent manifest
invalid_input400The run input does not satisfy the agent's input schema
invalid_manifest400Package ZIP import: manifest.json is not valid JSON or fails AFPS validation. Also a field code under validation_failed
invalid_path400Editing package files: a path is not a usable file path
invalid_request400Malformed request that does not fit field validation. param names the parameter at fault when there is one
invalid_signature401Run event ingestion: webhook-signature does not match the expected value
invalid_source400?source must be draft or published
invalid_stored_manifest500The catalog holds a malformed manifest for that package version
invalid_timestamp401Run event ingestion: webhook-timestamp is not a valid Unix timestamp
invalid_timezone400The schedule's timezone is not a valid time zone
INVALID_URL400GitHub import: the URL is not a GitHub URL this endpoint accepts
invalid_view_as400The X-View-As value cannot be parsed, or was sent as a header to a Server-Sent-Events route, which takes the view_as query parameter
invitation_accepted410The invitation was already accepted
invitation_already_pending409A pending invitation already exists for this email. Edit it instead
invitation_cancelled410The invitation was cancelled
invitation_expired410The invitation has expired
invitation_failed500The invitation could not be created or sent
invitation_not_found404No invitation matches that token
last_owner409An organization must keep at least one owner
locked_input_field400The input field is locked on this agent and cannot be set at launch
locked_required_field_empty400A required input field cannot be locked without a value
message_replayed409Run event ingestion: that webhook-id was already used for this run
method_not_allowed405Only from the MCP endpoint, on a method other than POST. An Allow header lists the permitted methods
missing_content400Package ZIP import: a required companion file is missing or empty, such as the agent's prompt.md
missing_integration_connection409The run cannot start because of its integrations. errors[] holds one entry per integration; see Connection and readiness entries
missing_manifest400Package ZIP import: the archive has no manifest.json, and is not a bare skill either
missing_signature_headers401Run event ingestion: webhook-id, webhook-timestamp and webhook-signature are all required
missing_skill400The agent declares a skill that is not available to it. Publish it, or share it with the agent's home space
model_already_added409The model is already added for this credential
model_credential_missing400The selected model has no API key on its provider credential
model_disabled409A disabled model cannot be the default, and the default model cannot be disabled
model_needs_reconnection409The model's provider credential must be reconnected before it can be the default
model_not_configured400No model is configured for the run
model_not_offered400The provider does not offer that model
model_provider_unregistered409The model is bound to a provider this instance does not register
name_collision409The package id is taken, or names a system package that cannot be overwritten
network_error502Live model search could not reach OpenRouter
no_active_subscription409Billing module. The organization has no subscription to change. Start a checkout instead
no_billing_account404Billing module. The organization has no billing account
no_changes409Nothing changed since the last published version
no_published_version404 / 409The package has never been published. 404 when running or exporting an agent (run the draft instead), 409 when enforcing a skill in a space's chat
not_a_space_member403The caller holds no role in the requested space
not_an_agent400The referenced package exists but is not an agent
not_cancellable409The run is no longer pending or running, so it cannot be cancelled
not_found, NOT_FOUND404 / 400The resource does not exist or is outside the caller's reach; the two are deliberately indistinguishable. NOT_FOUND (400) is a GitHub import whose repository or branch does not exist
OAUTH_CONNECTION_NEEDS_RECONNECTION410Sidecar. The model provider's OAuth credential is flagged for reconnection
OAUTH_REFRESH_REVOKED410Sidecar. The provider revoked the refresh token. The credential is flagged for reconnection
operation_not_allowed403The operation is not permitted on a built-in package or entity
org_creation_disabled403Organization creation is disabled on this instance
org_deleting409The organization is being deleted; no new work is admitted
org_not_found404The organization of the invitation no longer exists
org_run_concurrency_exceeded429The organization reached its concurrent-run cap. Wait for runs to finish
org_run_rate_limited429The organization reached its run launch rate. retry_after is set
organization_has_no_default_space409The package cannot be re-homed: the organization has no default space
package_archive_unreadable422A stored package archive expands past the decompression limit. Republish it
package_copy_restricted403The organization restricts copying packages out of their home space
package_has_no_version409The package has no published version to offer outside its home space
package_not_active_in_space404The package exists but is not active in this space
package_not_found404No package with that id in the caller's organization
package_not_placed404The package is not placed in this space
pairing_expired_or_consumed410The model-provider pairing token expired or was already used
password_already_set409The account already has a password. Use the change-password flow
path_conflict400Editing package files: a path conflicts with another file or directory, or a move target already exists
payload_too_large413The request body or an upload is over its size limit
payment_service_unavailable503Billing module. The payment service is unavailable
personal_space_has_no_members409A personal space takes no other members. Convert it to a team space first
personal_space_immutable409Only the name of a personal space can change
personal_space_not_deletable409A personal space is deleted by offboarding its owner, not directly
personal_space_not_orphaned409The personal space still has an owner in the organization
personal_space_takes_no_end_users409End-users are created in a team space, not a personal one
personal_space_takes_no_keys409API keys are created in a team space, not a personal one
personal_space_takes_no_oauth_clients409OAuth clients are registered in a team space, not a personal one
post_install_failed400The package could not be installed after its archive was accepted
precondition_failed412If-Match does not match the current ETag. The response carries the current ETag
precondition_required428The operation requires an If-Match header
provider_error502Live model search: OpenRouter returned an error
quota_exceeded402Billing module. The organization is out of credits
rate_limited, RATE_LIMITED429 / 400Too many requests: wait retry_after seconds. See Rate limiting. RATE_LIMITED (400) is a GitHub import that hit GitHub's own rate limit
reasoning_level_unsupported400The model does not support the requested reasoning level
reasoning_unsupported400The model does not support reasoning
redundant_space_role409The member's organization role already covers every space; an explicit space role would grant nothing
REPO_TOO_LARGE400GitHub import: the repository is too large to import directly
rerun_agent_mismatch409rerun_from names a run of a different agent
rerun_inline_input_unavailable409A field of the original run was an inline data: URI, which is not stored and cannot be replayed
reserved_entry400Editing package files: the manifest is edited through the package's manifest field, not as a file
role_in_use409The role is still held by space members or pending invitations
role_key_taken409A role with that key already exists in the organization
run_agent_deleted409Sidecar. The agent of the running run was deleted
run_definition_gone409Sidecar. The version the run executes is no longer readable
run_in_progress409The package still has runs in progress, so its runs cannot be deleted
run_not_running409The run is no longer running, so it cannot accept this operation
run_sink_closed410The run's event sink is closed. Stop sending events
run_sink_expired410The run's event sink expired
schedule_actor_invalid400The schedule's actor is not valid for this organization and space
schedule_modified_concurrently409The schedule changed while the request was handled. Reload and retry
share_target_is_home409The package already lives in that space
shutting_down503The instance is draining. Retry shortly; retry_after is set
signup_domain_not_allowed403Bootstrap: the owner's email domain is not on the signup allowlist (AUTH_ALLOWED_SIGNUP_DOMAINS). Another signup refusal keeps the code the auth layer gave it
skill_unchanged409The skill already exists with identical content
slug_taken400The organization slug is already in use
space_access_changed409The space's visibility or default role changed while the request was handled. Reload and retry
space_has_active_runs409The space cannot be deleted while runs are in progress
space_homes_packages409The space cannot be deleted while it is the home of packages
space_member_exists409The user already has an explicit space role. Use PATCH to change it
space_not_personal409The operation applies only to a personal space; this one is a team space
starting503The server is still starting up. Retry shortly
storage_limit_exceeded403The write would exceed the organization's storage quota (ORG_STORAGE_QUOTA_BYTES)
subscription_blocked402Billing module. The organization's subscription is suspended or cancelled
subscription_exists409Billing module. The organization already has a subscription. Change its plan instead of starting a checkout
temperature_unsupported400The model does not support a custom temperature
temperature_with_reasoning_unsupported400The model does not accept a temperature together with reasoning
timeout504Live model search timed out, or a connection attempt did not complete its login in time
timestamp_out_of_tolerance401Run event ingestion: webhook-timestamp is outside the accepted window
TOO_LARGE400GitHub import: the files exceed the total size limit
TOO_MANY_FILES400GitHub import: the path holds more files than the import accepts
tree_too_large413Editing package files: the package would exceed its total size limit
type_mismatch400The package already exists under another package type
unauthorized401No credential, or an invalid, revoked or expired one
unauthorized_target403Credential proxy: the target is outside the integration's authorized_uris, or none are declared
unexpected500Billing module. The usage check failed, so usage is refused
unresolved_placeholder400Credential proxy: the target, a header or the body holds a {{field}} placeholder the connection cannot fill
unsupported_api_version400Appstrate-Version names a version the server cannot serve
upload_already_written409The staged upload already has its content
upload_expired410The staged upload expired, or its reuse window elapsed
upload_staging_limit_exceeded429You hold the maximum number of active staged uploads (UPLOAD_MAX_ACTIVE_PER_ACTOR)
upstream_timeout504Credential or model proxy: the upstream did not answer in time
upstream_unreachable502Credential or model proxy: the upstream could not be reached
upstream_unresolvable502Credential or model proxy: the upstream host could not be resolved
usage_context_required400A call on a platform-provided model arrived without a valid run context (X-Run-Id)
usage_not_allowedVariesNot a code but a type: a chat turn refused by a module's usage check carries type https://docs.appstrate.dev/errors/usage-not-allowed, the module's own code (such as quota_exceeded) and the status the module chose, 403 when it names none. This body carries no instance or request_id
validation_failed400Body validation failed. Every offending field is in errors[]
version_artifact_unavailable422The published version exists but its stored artifact cannot be read
version_exists409That version is already published and immutable. Bump the version
version_not_found404The requested version of the package does not exist
version_not_higher409The version is lower than the highest published version
version_yanked410The requested version was yanked and can no longer be run
view_as_forbidden403Only an organization owner or administrator can preview a role, and only one that grants nothing they lack
view_as_not_found404The space, role or organization named by the role preview does not exist
view_as_unsupported400The role preview is not supported for this authentication method
webhook_limit_reached400The organization or space is at its webhook limit
wrong_package_type409The package is not an integration
zip_bomb400Package ZIP import: the archive expands past the decompression limit
zip_invalid400Package ZIP import: the file is not a readable ZIP archive

Connection and readiness entries

A run blocked by its integrations answers 409 missing_integration_connection with one errors[] entry per integration (field: integrations.<id>). Each entry carries its own code and, where it helps a client act without parsing detail, extra snake_case members. On the credential proxy, a resolution failure comes back with its entry code as the top-level code of a 409 instead (for example must_choose_connection), with the entry in errors[]. In the chat, a model whose subscription credential died answers 409 needs_reconnection.

Entry codeMeaningExtra members
not_connectedNo connection to this integration is accessible to the callerauth_key, required_scopes
must_choose_connectionSeveral connections match; the caller must pick onecandidate_connections (each with id, label, account_id, owned_by_actor, needs_reconnection)
needs_reconnectionThe connection exists but its credential is deadconnection_id, owned_by_actor, auth_key, required_scopes
insufficient_scopesThe connection lacks scopes the selected tools needconnection_id, missing_scopes, owned_by_actor, auth_key, required_scopes
auth_key_mismatchThe agent requires an auth the caller's connections do not userequired_auth_key, available_auth_keys
auth_key_serves_no_selected_toolThe auth the agent requires exposes none of its selected toolsrequired_auth_key
auth_serves_no_selected_toolA connection of the set uses an auth that exposes none of the selected toolsconnection_id
remote_binds_one_connectionA remote run binds one connection per integration, but the connection choice binds several to this one. Narrow it to one with a member pin, or run the agent on the platform
pinned_connection_unavailableThe connection pinned for this agent is gone or unusable
override_connection_unavailableThe connection passed as a per-run override is gone or unusable
override_outrankedThe per-run override falls outside the pin or organization default that governs the integration
integration_not_foundThe agent declares an integration that does not exist
integration_wrong_typeA package declared as an integration is another package type
integration_invalid_manifestA declared integration's manifest fails validation
integration_not_activeA declared integration is not active in this space
agent_not_activeOnly in the readiness read, with field: agent: the space has switched the agent off. A run launch answers 404 agent_not_active_in_space instead

When the caller opted in to connect offers, an entry that an OAuth connect flow can clear also carries connect_url, expiresAt and packageId: a single-use, short-lived link to open, never to store.

AFPS runtime error codes

These come from @appstrate/afps-runtime, the portable bundle runner behind the standalone afps CLI. They are not platform API errors:

  • Calling the HTTP API? You will not see them. The platform translates bundle failures at its boundary: DEPENDENCY_UNRESOLVED becomes dependency_unresolved, INTEGRITY_MISMATCH becomes bundle_integrity_mismatch, any other bundle code becomes bundle_invalid, and a signature policy failure becomes bundle_signature_invalid.
  • Running the afps CLI, or embedding the runtime? These are the codes you will see. The runtime throws typed errors (BundleError, BundleSignaturePolicyError, ApiCallFailureError, ResolverError), each with a code, a message, and optional structured details. They are not problem documents and carry no type. The CLI prints a failure as message [CODE].

Bundle assembly

Raised by BundleError while reading, verifying or building a bundle.

CodeMeaning
ARCHIVE_INVALIDThe archive cannot be decompressed, or holds an unsafe path (absolute, backslash, traversal)
BUNDLE_JSON_MISSINGThe archive contains no bundle.json
BUNDLE_JSON_INVALIDbundle.json is not valid JSON, or declares an invalid package identity or path
RECORD_MISSINGA package in the bundle has no RECORD file
RECORD_MALFORMEDA RECORD line cannot be parsed
RECORD_MISMATCHA file is missing from RECORD, listed but absent, or its size or hash does not match
INTEGRITY_MISMATCHStored bytes no longer hash to the integrity value recorded at publish time: corruption or tampering. Over HTTP it becomes bundle_integrity_mismatch, not the unrelated API code integrity_mismatch
VERSION_UNSUPPORTEDbundleFormatVersion is outside the versions this runtime reads or writes
LIMITS_EXCEEDEDThe bundle exceeds a configured limit (compressed size, file count, and similar)
DEPENDENCY_UNRESOLVEDA declared dependency resolves to nothing, or to a version whose bytes cannot be loaded. Over HTTP it becomes dependency_unresolved

Bundle validation

Reported by bundle validation, which afps verify prints as [CODE] path: message. The last two are warnings and do not make a bundle invalid.

CodeMeaning
MANIFEST_SCHEMAA package manifest fails AFPS schema validation
UNSUPPORTED_TYPEThe root package is not an agent, or a package has an unknown type
TEMPLATE_SYNTAXThe agent's prompt.md is not a valid Mustache template
COMPANION_FILE_MISSINGA file the package type requires is missing or empty
SCHEMA_VERSION_MISSINGThe agent manifest declares no schema_version
SCHEMA_VERSION_UNSUPPORTEDThe manifest's schema_version is not a supported major
CYCLE_DETECTEDWarning: the dependency graph has a cycle
VERSION_DIVERGENCEWarning: the bundle holds several versions of one package

Signatures and trust chains

Raised by BundleSignaturePolicyError under a required signature policy. Under warn, the same reasons are reported to a callback instead of thrown. These codes are lower case.

CodeMeaning
signature_invalidThe Ed25519 signature does not verify against the bundle
alg_unsupportedThe signature declares an algorithm other than ed25519
chain_untrustedThe chain ends at a key that is not in the trust root
chain_invalidThe chain is structurally broken
chain_missingThe signing key is not in the trust root and the bundle supplies no chain reaching it
malformedThe signature document cannot be parsed
unsignedThe bundle carries no signature. Reported under warn, never thrown
unsigned_requiredThe bundle carries no signature and the policy is required, or no trust root was given

Outbound calls and resolvers

Raised when an integration's api_call tool is prepared or executed. ApiCallFailureError carries the lower-case codes the platform's credential proxy answers, with the same meaning; details holds the integration, the target as written and the declared allowlist. ResolverError carries the RESOLVER_ codes.

CodeMeaning
unauthorized_targetThe target, or a redirect hop, is outside the integration's authorized_uris, or the connection does not render the declared list. As the API's unauthorized_target
blocked_targetThe target, or a redirect hop, resolves to a blocked (private or reserved) address. As the API's blocked_target
credential_exfiltration_refusedThe call carries a credential but the authorized_uris do not bind the target's host, a host wildcard included. As the API's credential_exfiltration_refused
credential_unusableA header value holding the substituted or injected credential is not a valid HTTP field value. Reconnect the connection
upstream_unresolvableThe target host could not be resolved
upstream_unreachableThe target could not be reached
upstream_timeoutThe target did not answer in time
RESOLVER_MISSING_REQUIREDA body that reads a workspace file was sent to a resolver that has no workspace
RESOLVER_BODY_REFERENCE_FORBIDDENThe body references a workspace file but the resolver does not allow file access
RESOLVER_BODY_TOO_LARGEThe request body exceeds the size limit
RESOLVER_BODY_INVALIDThe api_call arguments are invalid, the body cannot be built as declared, or a {{field}} placeholder cannot be filled from the connection
RESOLVER_HEADER_INVALIDA header value the agent sent is not a valid HTTP field value
RESOLVER_PATH_OUTSIDE_ALLOWED_ROOTSA referenced file path is not inside the workspace
RESOLVER_PATH_SYMLINK_REFUSEDThe runtime refused to read or write through a symlink
RESOLVER_PATH_INVALIDA referenced file path is empty or not a string

Retrying

  • 429 or 503 with retry_after: wait that many seconds (the Retry-After header carries the same value), then retry. rate_limited, org_run_rate_limited, chat_capacity and shutting_down set it.
  • 429 without it (org_run_concurrency_exceeded, upload_staging_limit_exceeded) and 503 starting: back off and retry once in-flight work has finished or the server is up.
  • 5xx without a hint: retry with exponential backoff and jitter, starting around one second and capped at one minute. For a POST, send an Idempotency-Key so a retry cannot create a duplicate. Only the operations that declare the header accept it.
  • 409 idempotency_in_progress: retry shortly with the same key. 422 idempotency_conflict: do not retry; that key belongs to a different request.
  • Other 4xx: do not retry unchanged. The request, or the state it depends on, needs to change first. A few 409 codes (conflict, schedule_modified_concurrently, space_access_changed) ask you to reload the resource and try again.

Examples

Authentication failure

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json
WWW-Authenticate: Bearer error="invalid_token"
{
  "type": "https://docs.appstrate.dev/errors/unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid or expired API key",
  "instance": "urn:appstrate:request:req_…",
  "code": "unauthorized",
  "request_id": "req_…"
}

Rate limited

HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 42
RateLimit: limit=20, remaining=0, reset=42
RateLimit-Policy: 20;w=60
{
  "type": "https://docs.appstrate.dev/errors/rate-limited",
  "title": "Rate Limited",
  "status": 429,
  "detail": "Too many requests. Please try again shortly.",
  "instance": "urn:appstrate:request:req_…",
  "code": "rate_limited",
  "request_id": "req_…",
  "retry_after": 42
}

A header the credential cannot honor

{
  "type": "https://docs.appstrate.dev/errors/header-not-allowed",
  "title": "Header Not Allowed",
  "status": 400,
  "detail": "Appstrate-User is only supported with api_key auth, not session authentication.",
  "instance": "urn:appstrate:request:req_…",
  "code": "header_not_allowed",
  "request_id": "req_…",
  "param": "Appstrate-User"
}

On this page