Errors
Handle AgentFS problem responses by status, code, detail, and retryability.
The API and hosted-file URLs return errors as application/problem+json. This example uses the /v1 response shape. Hosted-file URLs use a short phrase for title—such as Not found, Method not allowed, Precondition failed, Range not satisfiable, Region unavailable, or Storage unavailable—while code stays snake_case. The response has this shape:
{
"type": "https://docs.agentfs.cloud/errors#storage-limit-exceeded",
"title": "storage_limit_exceeded",
"status": 413,
"code": "storage_limit_exceeded",
"detail": "This upload would exceed the account storage limit of 2147483648 bytes.",
"retryable": false
}detail is optional. retryable is true for responses with a status of 500 or higher, and for 429. Every 429 and 503 also carries a Retry-After header in seconds. The server also sends Cache-Control: no-store.
Handle an error
Use the response as a machine-readable instruction. An agent should branch on status and code, then keep detail for a user or log message.
| Status | Typical cause | What to do | Retry? |
|---|---|---|---|
400, 401, 403, 404, 409, 412, 413, 422, 451 | Bad input, missing access, a state conflict, or a blocked file. | Correct the request, refresh the file state, or ask for access. | No. |
408 | The request took too long to arrive. | Retry with exponential backoff. | Yes. |
429 | A request or upload limit was reached. | Wait for Retry-After, then retry the same safe request. | Yes, after waiting. |
500–599 | AgentFS or its storage dependency could not finish the operation. | Use bounded exponential backoff, and wait for Retry-After on a 503. Preserve the idempotency key for writes. | Yes. |
For a complete retry policy and safe-write examples, see Retries and recovery.
The table lists codes declared by the API, upload handling, and hosted-file responses. Each code has its own section below so you can link directly to it.
| Code | Status | Meaning |
|---|---|---|
| access_denied | 403 | Device access was denied. |
| api_key_create_failed | 500 | The dashboard could not create a key. |
| api_key_not_found | 404 | The key to revoke was not found. |
| api_key_required | 400 | An upload without a key asked for an option that needs one. |
| authorization_pending | 428 | The device has not been approved. |
| batch_create_failed | 500 | The batch record could not be created. |
| batch_item_create_failed | 500 | A batch item record could not be created. |
| batch_not_found | 404 | The batch does not exist or has expired. |
| batch_too_large | 413 | The batch body or file bytes exceed the limit. |
| batch_update_failed | 500 | The batch result could not be saved. |
| billing_customer_failed | 500 | The billing customer could not be created. |
| billing_customer_not_found | 404 | The account has no billing customer. |
| billing_unavailable | 503 | The account storage limit cannot be determined. |
| checkout_unavailable | 502 | A checkout URL could not be created. |
| checksum_mismatch | 422 | The completed object hash does not match the declared hash. |
| csrf_rejected | 403 | A dashboard mutation came from the wrong web origin. |
| device_authorization_failed | 500 | The device request could not be created. |
| expired_device_code | 400 | The device code has expired. |
| file_blocked | 451 | The file was blocked for abuse. |
| file_not_found | 404 | The file is missing, expired, or inaccessible. |
| file_too_large | 413 | A one-shot upload exceeds its 100 MiB limit. |
| folder_create_failed | 500 | A folder for the path could not be created. |
| forbidden | 403 | The key or session lacks permission. |
| idempotency_conflict | 409 | A reused idempotency key has different content. |
| internal_error | 500 | The request could not be completed. |
| invalid_agent_label | 400 | An agent, run, or session label is invalid. |
| invalid_api_key | 400 | The API-key request body is invalid. |
| invalid_billing_request | 400 | The billing request is not valid JSON or has invalid fields. |
| invalid_device_code | 400 or 404 | The device code or user code is invalid. |
| invalid_expiration | 400 | An access duration is invalid. |
| invalid_manifest | 400 | The batch manifest is invalid. |
| invalid_metadata | 400 | An upload metadata value is invalid. |
| invalid_part | 400 | A multipart part number or size is invalid. |
| invalid_path | 400 | A path or file name is invalid. |
| invalid_project | 400 | A project name or project request is invalid. |
| invalid_project_settings | 400 | Project settings are invalid. |
| invalid_project_transfer | 400 | The project transfer request is invalid. |
| invalid_query | 400 | A query parameter is invalid. |
| invalid_signature | 400 | A billing webhook signature is invalid. |
| invalid_token | 400 or 401 | An upload service token is missing or invalid. |
| invalid_upload | 400 | The upload-session request is invalid. |
| jurisdiction_immutable | 409 | A project's storage region cannot be changed. |
| length_required | 411 | A raw part is missing Content-Length. |
| method_not_allowed | 405 | The hosted-file URL only allows GET and HEAD. |
| missing_parts | 409 | Not every multipart part is present. |
| missing_signature | 400 | A billing webhook has no signature. |
| no_active_organization | 403 | The dashboard session has no active organization. |
| object_not_ready | 409 | A direct multipart object is not ready to finalize. |
| part_upload_failed | 502 | A multipart part could not be stored. |
| path_exists | 409 | The requested path is already used. |
| plan_unavailable | 400 | The plan cannot be bought right now. |
| precondition_failed | 409 or 412 | A request's If-Match or if_updated_at did not match. |
| project_create_failed | 500 | The project could not be created. |
| project_exists | 409 | A project with that name already exists. |
| project_not_empty | 409 | The project still contains files. |
| project_not_found | 404 | The project does not exist or is not visible to the key. |
| project_update_failed | 500 | The project could not be updated. |
| range_not_satisfiable | 416 | A hosted-file byte range is outside the file. |
| rate_limited | 429 | The caller sent too many requests. |
| region_unavailable | 422 or 503 | 422 for account/API requests; 503 for hosted-file URLs. |
| size_mismatch | 409 or 422 | 409 for recorded part sizes; 422 for a completed object size mismatch. |
| slow_down | 429 | Poll the device endpoint less often. |
| storage_delete_failed | 502 | The stored object could not be deleted. |
| storage_limit_exceeded | 413 | The upload would exceed account storage. |
| storage_unavailable | 502 or 503 | 503 from /v1; 502 from hosted-file URLs. |
| subscription_exists | 409 | The account already has a subscription. |
| unauthorized | 401 | Authentication is missing or invalid. |
| unknown_bucket | 400 | The selected storage target is unknown. |
| upload_abort_failed | 502 | The multipart upload could not be aborted. |
| upload_complete_failed | 502 | The upload could not be completed. Safe to retry. |
| upload_completed | 409 | The multipart session is already complete. |
| upload_create_failed | 500 or 502 | The multipart session could not be started. |
| upload_expired | 410 | The multipart session has expired. |
| upload_failed | 500 or 502 | The file upload failed. |
| upload_not_active | 409 | The session is not ready for this operation. |
| upload_not_found | 404 | The upload session does not exist. |
| upload_not_pending | 409 | The upload session is no longer waiting for completion. |
| upload_not_started | 409 | The direct multipart upload has not started. |
| upload_rate_limited | 429 | The account has too many active uploads. |
| upload_unavailable | 503 | Upload storage is unavailable right now. Retry shortly. |
access_denied
403 — The device owner denied the request, or the approved device cannot be claimed.
api_key_create_failed
500 — The dashboard could not save a new API key.
api_key_not_found
404 — The API key ID to revoke does not exist in the active account.
api_key_required
400 — An upload without a key sent an option that needs one, such as path, expires_in, visibility=private, or an Idempotency-Key. Send the request with an API key, or drop the option. See Upload without a key.
authorization_pending
428 — The device code is valid but the account owner has not approved it. Wait for Retry-After and poll again.
batch_create_failed
500 — The batch record could not be created.
batch_item_create_failed
500 — An item record could not be created while the batch was being processed.
batch_not_found
404 — The batch ID is missing or its 24-hour retention period has ended.
batch_too_large
413 — The batch request body or summed file bytes exceed 104,857,600 bytes.
batch_update_failed
500 — AgentFS could not save the final batch counts.
billing_customer_failed
500 — AgentFS could not create the account's billing customer.
billing_customer_not_found
404 — The account has no billing customer for the requested billing portal action.
billing_unavailable
503 — AgentFS cannot determine the account's plan storage limit.
checkout_unavailable
502 — AgentFS did not receive a checkout URL.
checksum_mismatch
422 — The completed object hash does not match the SHA-256 value declared when the session was created.
cross_region_copy_unsupported
409 — Source and destination are in different storage regions. Bytes are not copied across regions.
csrf_rejected
403 — A dashboard cookie mutation did not come from the AgentFS web origin.
device_authorization_failed
500 — AgentFS could not create a device authorization request.
expired_device_code
400 — The device code or user code is past its expiry. Start the flow again.
file_create_failed
500 — The file record could not be created. Nothing was stored. Retry the request.
file_blocked
451 — The file was blocked for abuse, so its hosted URL no longer serves the bytes. The identifier is in code; title is File blocked. Do not retry. To report a file, use the Report link on its page or write to abuse@agentfs.cloud.
file_not_found
404 — The file is missing, not ready, expired, or outside the key's read scope. Public file URLs use the same code when no file is available, and for a private link that was revoked. Restoring a file that was purged or permanently deleted also returns this code.
file_too_large
413 — A one-shot upload over 104,857,600 bytes (100 MiB) is too large. An upload session declaring more than 5,000,000,000,000 bytes (5 TB) returns 400 invalid_upload instead.
folder_create_failed
500 — AgentFS could not create a folder required by the file path.
forbidden
403 — The API key or dashboard session does not grant the requested read, write, delete, or share permission, or the action needs a team owner or admin (deleting or transferring a project, billing, revoking someone else's key).
idempotency_conflict
409 — The idempotency key already belongs to a different request. Use a new key or send the original request unchanged.
internal_error
500 — The request hit an unexpected server error. The response is retryable.
invalid_agent_label
400 — X-Agent-ID, X-Run-ID, or X-Session-ID is empty, too long, or contains a control character.
invalid_api_key
400 — The dashboard API-key request body is invalid JSON or has invalid fields.
invalid_billing_request
400 — The billing request is not valid JSON or does not match the accepted schema.
invalid_device_code
400 or 404 — The device code is unknown, already claimed, or the approval user code is not valid.
invalid_expiration
400 — The signed-link duration is malformed or longer than 7 days.
invalid_manifest
400 — The manifest is not a JSON array, has invalid entries, repeats a part name, omits a file part, or has more than 25 entries.
invalid_metadata
400 — A multipart upload field, label, visibility, disposition, or expiration value is invalid.
invalid_part
400 — The part number is not a positive integer, is outside the session, or has an invalid size.
invalid_path
400 — A path has an invalid segment, missing filename, bad encoding, or a filename outside the selected project.
invalid_project
400 — The project name contains unsupported characters or the project parameter is malformed.
invalid_project_settings
400 — Project settings are empty or contain an invalid name, visibility, or expiry.
invalid_project_transfer
400 — A project transfer targets the same organization.
invalid_query
400 — A list limit or cursor is malformed or outside its accepted range.
invalid_signature
400 — A billing webhook signature does not verify.
invalid_token
400 or 401 — An upload service token is missing, malformed, expired, or does not match the requested operation.
invalid_upload
400 — The upload-session body is not valid JSON or does not match its schema.
jurisdiction_immutable
409 — A project's storage region is fixed after creation.
length_required
411 — A raw multipart part must include Content-Length.
misconfigured
503 — A delivery host is missing required configuration, such as its signing secret. This is on our side. It is retryable: true; nothing about your request needs to change.
method_not_allowed
405 — A hosted-file URL accepts only GET and HEAD requests.
missing_parts
409 — Completion requires every part from 1 through total_parts.
missing_signature
400 — A billing webhook request has no signature header.
no_active_organization
403 — Sign in with a session that has an active organization.
object_not_ready
409 — A direct multipart upload has not finished writing its object. Wait for the transfer to finish, then finalize it.
part_upload_failed
502 — The upload service could not store a multipart part. Retry the part with the same part number.
path_exists
409 — A file or folder already uses the requested path. A restore returns it when another live file now holds the deleted file's path.
plan_unavailable
400 — Checkout for this plan is not open yet. Choose another plan.
precondition_failed
409 or 412 — A conditional request did not match the stored file.
412 means the precondition you sent was already wrong: If-Match did not equal the file's current etag on POST /v1/files with if_exists=replace, or If-Match was missing or stale on POST /v1/files/:id/replace. A hosted-file URL returns 412 for a failed If-Match or If-Unmodified-Since too.
409 means the file changed between the read and the write: a replace move whose If-Match or if_updated_at no longer holds, or a replacement that lost a race with a concurrent one. Read the file again and retry against the new etag.
project_create_failed
500 — AgentFS could not save the project.
project_exists
409 — The organization already has a project with this name.
project_not_empty
409 — Delete the project's files first, or pass recursive=true.
project_not_found
404 — The project name or ID does not exist, or the key cannot read it.
project_update_failed
500 — AgentFS could not save the project update.
range_not_satisfiable
416 — A hosted-file request asked for bytes outside the file. The response includes the available size in Content-Range.
rate_limited
429 — The caller went over a request rate limit: /v1 calls per API key, uploads without a key per IP address, or device codes per IP address. The response is retryable: true and carries a Retry-After header in seconds. Wait for it, then send the same request again. See Limits for the numbers.
region_unavailable
422 or 503 — An account or /v1 API request returns 422 when the requested storage region is unavailable. A hosted-file URL returns 503 when the file's region is not bound.
size_mismatch
409 or 422 — 409 means the recorded part sizes do not add up to expected_size_bytes; 422 means the completed object size does not match it.
slow_down
429 — The device token was polled before its interval elapsed. Wait for Retry-After seconds.
source_unavailable
The url given to POST /v1/files could not be fetched: it redirected, answered with an error, or did not respond within 60 seconds. Fix the source and send the request again; it is not retried automatically.
storage_copy_failed
502 — Storage could not copy the bytes. Nothing was kept. It is retryable: true.
storage_delete_failed
502 — AgentFS could not remove the stored bytes while deleting a file or project.
storage_limit_exceeded
413 — The upload would exceed the account's hard storage cap. The detail includes the cap in bytes. For an upload without a key, the IP address already holds 500 MB of unexpired files; wait for some to expire or use a key.
storage_unavailable
502 or 503 — /v1 returns 503 when AgentFS cannot reach the stored object; a hosted-file URL returns 502 for the same code.
subscription_exists
409 — The account already has a subscription. Manage that subscription instead of starting another checkout.
unauthorized
401 — Send a valid bearer key, or sign in for a dashboard request. Revoked and expired keys also use this code.
unknown_bucket
400 — The upload service does not recognize the selected storage target. Check the project's storage region.
upload_abort_failed
502 — AgentFS could not abort the multipart upload.
upload_complete_failed
502 — The upload could not be completed. retryable is true: call POST /v1/uploads/:id/complete again. If it already finished, the retry succeeds.
upload_completed
409 — The upload session is already complete and cannot be aborted.
upload_create_failed
500 or 502 — AgentFS could not create the multipart session or its underlying multipart upload.
upload_expired
410 — The session expiry passed. Create a new session.
upload_failed
500 or 502 — A file upload or multipart completion failed. Check detail before retrying.
upload_not_active
409 — The session is not in a state that accepts the requested operation.
upload_not_found
404 — The upload session ID does not exist, belongs to another account, or expired before completion.
upload_not_pending
409 — The file is no longer waiting for the multipart completion operation.
upload_not_started
409 — The direct session has no multipart upload ID yet. Start it first.
upload_rate_limited
429 — The account already has 64 active upload sessions. Finish or abort one before creating another. The response carries Retry-After.
upload_unavailable
503 — Uploads are temporarily unavailable. retryable is true: wait for Retry-After and retry.
AgentFS