# Error codes

> Every Guv error code, what it means and how to fix it.

When something goes wrong, Guv answers with an RFC 9457 `application/problem+json` body. It always has a stable `code`, a plain-English `detail`, a suggested `fix` and a `docs_url` pointing at one of these pages. The `guv` CLI exits with a predictable code too, so scripts and agents can react without parsing text.

| Exit code | Meaning |
| --- | --- |
| `0` | success |
| `1` | not signed in |
| `2` | the request or file is not valid |
| `3` | forbidden or denied by policy |
| `4` | approval required |
| `5` | quota or spend cap |
| `10` | temporary, safe to retry |

## Request problems

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`bad_request`](/errors/bad_request/) | 400 | The request could not be read | 2 |
| [`validation_failed`](/errors/validation_failed/) | 422 | Some fields are not valid | 2 |
| [`manifest_invalid`](/errors/manifest_invalid/) | 422 | guv.yaml is not valid | 2 |
| [`reference_unresolved`](/errors/reference_unresolved/) | 422 | A reference could not be resolved | 2 |
| [`secret_missing`](/errors/secret_missing/) | 422 | A secret this needs has not been set | 2 |
| [`confirmation_required`](/errors/confirmation_required/) | 422 | This destructive action needs a typed confirmation | 2 |
| [`immutable_field`](/errors/immutable_field/) | 422 | This field cannot be changed after creation | 2 |
| [`volume_shrink_not_supported`](/errors/volume_shrink_not_supported/) | 422 | Volumes can grow but not shrink | 2 |
| [`unsupported_api_version`](/errors/unsupported_api_version/) | 400 | Unknown Guv-Version | 2 |
| [`idempotency_key_required`](/errors/idempotency_key_required/) | 400 | Idempotency-Key header is required | 2 |
| [`idempotency_key_reused`](/errors/idempotency_key_reused/) | 422 | This Idempotency-Key was already used for a different request | 2 |
| [`method_not_allowed`](/errors/method_not_allowed/) | 405 | Method not allowed | 1 |
| [`not_acceptable`](/errors/not_acceptable/) | 406 | The requested media type is not available | 1 |
| [`payload_too_large`](/errors/payload_too_large/) | 413 | The request body is too large | 2 |
| [`unsupported_media_type`](/errors/unsupported_media_type/) | 415 | Unsupported Content-Type | 2 |

## Signing in and permissions

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`unauthorized`](/errors/unauthorized/) | 401 | You are not signed in | 1 |
| [`token_expired`](/errors/token_expired/) | 401 | Your token has expired | 1 |
| [`passkey_verification_failed`](/errors/passkey_verification_failed/) | 401 | The passkey check did not pass | 1 |
| [`challenge_expired`](/errors/challenge_expired/) | 400 | The sign-in or approval challenge has expired | 2 |
| [`mfa_required`](/errors/mfa_required/) | 403 | This workspace requires a passkey on every member account | 3 |
| [`forbidden`](/errors/forbidden/) | 403 | You do not have permission to do this | 3 |
| [`dev_login_disabled`](/errors/dev_login_disabled/) | 404 | Development login is turned off | 1 |

## Agents, approvals and policy

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`scope_missing`](/errors/scope_missing/) | 403 | This token lacks a required scope | 3 |
| [`approval_required`](/errors/approval_required/) | 403 | A human needs to approve this | 4 |
| [`agent_budget_exceeded`](/errors/agent_budget_exceeded/) | 403 | This would raise spend beyond the agent token's daily budget | 4 |
| [`policy_denied`](/errors/policy_denied/) | 403 | A policy does not allow this | 3 |
| [`deploy_blocked`](/errors/deploy_blocked/) | 422 | The deploy was blocked by a safety check | 3 |
| [`change_freeze`](/errors/change_freeze/) | 403 | A change freeze is in effect | 3 |

## Spend, plans and limits

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`workspace_suspended`](/errors/workspace_suspended/) | 403 | This workspace is suspended | 3 |
| [`spend_cap_reached`](/errors/spend_cap_reached/) | 402 | The spend cap has been reached | 5 |
| [`payment_required`](/errors/payment_required/) | 402 | A payment method or plan upgrade is needed | 5 |
| [`quota_exceeded`](/errors/quota_exceeded/) | 403 | A plan limit has been reached | 5 |

## Conflicts and state

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`plan_hash_mismatch`](/errors/plan_hash_mismatch/) | 409 | The live state changed since this plan was made | 2 |
| [`idempotency_request_in_progress`](/errors/idempotency_request_in_progress/) | 409 | A request with this Idempotency-Key is still running | 10 |
| [`not_found`](/errors/not_found/) | 404 | Not found | 1 |
| [`conflict`](/errors/conflict/) | 409 | This conflicts with the current state | 1 |
| [`already_exists`](/errors/already_exists/) | 409 | Something with this name already exists | 2 |
| [`domain_taken`](/errors/domain_taken/) | 409 | This hostname is in use by another project | 2 |
| [`domain_verification_failed`](/errors/domain_verification_failed/) | 422 | The DNS records are not right yet | 2 |
| [`operation_not_cancellable`](/errors/operation_not_cancellable/) | 409 | This operation can no longer be cancelled | 1 |
| [`deployment_not_rollbackable`](/errors/deployment_not_rollbackable/) | 409 | This deployment cannot be rolled back to | 1 |
| [`precondition_failed`](/errors/precondition_failed/) | 412 | The resource changed since you read it | 1 |

## CLI sign-in (device flow)

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`authorization_pending`](/errors/authorization_pending/) | 400 | Waiting for the user to approve this device | 10 |
| [`slow_down`](/errors/slow_down/) | 400 | Polling too fast | 10 |
| [`access_denied`](/errors/access_denied/) | 400 | The user denied the request | 1 |
| [`expired_token`](/errors/expired_token/) | 400 | The device code has expired | 1 |

## Sign-ups, invitations and the waitlist

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`signups_closed`](/errors/signups_closed/) | 403 | Sign-ups are closed | 1 |
| [`invite_required`](/errors/invite_required/) | 403 | An invitation is required | 1 |
| [`invite_invalid`](/errors/invite_invalid/) | 404 | The invitation is not valid | 1 |
| [`invite_expired`](/errors/invite_expired/) | 410 | The invitation has expired | 1 |
| [`invite_used`](/errors/invite_used/) | 409 | The invitation has already been used | 1 |
| [`invite_revoked`](/errors/invite_revoked/) | 410 | The invitation was revoked | 1 |
| [`bot_check_failed`](/errors/bot_check_failed/) | 422 | The bot check failed | 2 |
| [`terms_not_accepted`](/errors/terms_not_accepted/) | 422 | Current terms must be accepted | 2 |
| [`link_expired`](/errors/link_expired/) | 410 | This link has expired | 1 |

## Platform and transient errors

| Code | Status | Means | Exit |
| --- | --- | --- | --- |
| [`kill_switch_engaged`](/errors/kill_switch_engaged/) | 403 | The AI operations kill switch is on; agents are read-only | 3 |
| [`rate_limited`](/errors/rate_limited/) | 429 | Too many requests | 10 |
| [`internal_error`](/errors/internal_error/) | 500 | Something went wrong on our side | 10 |
| [`transient`](/errors/transient/) | 503 | A dependency is briefly unavailable | 10 |
| [`timeout`](/errors/timeout/) | 504 | The request took too long | 10 |
