---
title: "Errors"
description: "How Frazer reports errors in the API, CLI, MCP and console, with status codes, reasons, decision IDs you can replay, remedies and Request access, and every error reason with its fix."
url: https://frazer.ai/docs/errors
index: https://frazer.ai/docs/api/llms.txt
tokens: 5994
---

> For agents: this page is indexed at https://frazer.ai/docs/api/llms.txt, and every page at https://frazer.ai/llms.txt. Copy commands from this file exactly.

# Errors

Every Frazer error has the same shape wherever you meet it, in the API, the CLI, an MCP tool or the console: a status code, a machine-readable reason from a named service, metadata, and links to the fix. A permission denial also carries a decision ID that you can replay later to see exactly what decided it, even after the rules changed.

## The error model

| Part | What it is |
| --- | --- |
| **Code** | The status: `PERMISSION_DENIED`, `NOT_FOUND`, `RESOURCE_EXHAUSTED` and so on. It sets the HTTP status and the CLI’s exit code |
| **Reason** | A stable name in upper snake case, such as `QUOTA_EXCEEDED`. Reasons are added over time and never removed or reused |
| **Domain** | The service that answered: `<service>.frazer.ai`, such as `iam.frazer.ai`, `quota.frazer.ai` or `approvals.frazer.ai`. A reason is unique within its domain |
| **Metadata** | Facts about this error as a string map with camel-case keys, such as `quotaMetric` or `decisionId` |
| **Details** | `BadRequest` lists each invalid field; `QuotaFailure` gives the limit and usage; `RetryInfo` says when to retry; `Help` links to the fix |
| **Message** | One sentence for people. Programs should read the code, reason and domain, not the message |

Codes map to HTTP and to the [CLI’s exit codes](https://frazer.ai/docs/cli#exit-codes):

| Code | HTTP | CLI exit | Typical cause |
| --- | --- | --- | --- |
| `INVALID_ARGUMENT` | 400 | 2 | A field is missing or invalid; `BadRequest` names it |
| `UNAUTHENTICATED` | 401 | 4 | No credential, or one that expired or was revoked |
| `PERMISSION_DENIED` | 403 | 4 | Not allowed, or the resource does not exist |
| `NOT_FOUND` | 404 | 3 | The resource does not exist |
| `ALREADY_EXISTS` | 409 | 5 | A resource with this ID exists |
| `FAILED_PRECONDITION` | 400 | 5 | Not possible in the current state: a project deleted, a lien, no Fir session |
| `ABORTED` | 409 | 5 | A concurrent change: a stale etag or a changed plan |
| `RESOURCE_EXHAUSTED` | 429 | 6 | A quota, rate or concurrency limit, or overload: time fixes it |
| `UNAVAILABLE` | 503 | 7 | Frazer could not answer; retry |
| `DEADLINE_EXCEEDED` | 504 | 7 | The call ran out of time |

A run that stops at a limit you set (`TURN_LIMIT_REACHED`, `STEP_LIMIT_REACHED`, `BUDGET_REACHED`) exits `14` and keeps its result. A reason that needs a person exits `13`, **needs a person**, whatever its code: `APPROVAL_REQUIRED`, `FUNDS_EXHAUSTED` (money that ran out, HTTP 402, never `6`), `PERSON_TASK_REQUIRED` and `PERSON_REQUIRED`. Every error’s `next` names what to follow. The CLI’s own refusals before a call, such as an unknown command or `CONFIRMATION_REQUIRED`, exit `2`.

## Permission denials

A denial is `PERMISSION_DENIED` in domain `iam.frazer.ai`. Its message ends “(or it might not exist)”: a resource you may not see and a resource that does not exist get the same answer, so a denial never reveals what exists. Its metadata:

| Key | Meaning |
| --- | --- |
| `permission` | The permission the method needed, such as `kubernetes.clusters.get` |
| `resource` | The resource, as you named it |
| `deniedBy` | Which check refused (below) |
| `decisionId` | The ID of this decision, to replay it |
| `rolesGranting` | Up to three of the smallest predefined roles that include the permission, comma-separated |

Its `Help` links to the access troubleshooter for this decision. Nothing else about your organization’s rules is in the error: no rule names and no other members.

| `deniedBy` | What refused |
| --- | --- |
| `kill_switch` | The organization suspended its agents |
| `token_scope` | The token’s scopes or its organization do not cover the call |
| `deny` | A deny rule |
| `principal_boundary` | A boundary that caps what this principal may do |
| `agent_ceiling` | The agent’s ceiling role |
| `allow` | No role you hold grants the permission |
| `condition` | A condition on a rule was not met or could not be checked |
| `session_assurance` | The method needs a recent passkey or second factor |
| `workload_only` | The method is only for Frazer’s own systems |

New values may appear; treat one you do not know as a plain denial.

### Explain a denial

Every response carries the decision ID in the `X-Frazer-Authz-Decision` header, allowed or not, and a denial repeats it in `decisionId`. Give it to the troubleshooter:

#### Console

Open the `Help` link of the error, `https://console.frazer.ai/iam/troubleshoot?decision=<decision ID>`.

#### CLI

The CLI prints a `troubleshoot:` line under every denial. Run it as printed; it calls `frazer iam decisions troubleshoot-access` and waits if the decision is not logged yet.

#### API

```sh
curl -X POST https://api.frazer.ai/iam/v1/organizations/acme/decisions:troubleshootAccess \
  -H "Authorization: Bearer $FRAZER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"decision": "0192f3a1-7c2e-7b4d-9e10-3f6a2b8c5d47"}'
```

#### MCP

Call `iam_troubleshoot_access` with the decision ID.

The troubleshooter replays the decision on your organization’s access rules as they were when it was made, and decides it again on the rules as they are now. It answers:

-   **The outcome** then, replayed and now, and whether the replay reproduced it.
-   **Each check in order** (the kill switch, token scope, deny rules, boundaries, allow) with its result, for you and for each principal an agent acted for.
-   **When access ends**, if it is allowed through something that expires.
-   **Remedies**, in this order: an eligible just-in-time entitlement; **Grant**, only if you may grant that role there; **Request access**, which files a request with the decision ID to your organization’s IAM contacts.

There are two views. Anyone can explain their own decisions and their agents’ decisions; that view shows the roles that would grant access and remedies that name no one, and says whether an IAM change since then affects it. Administrators with `roles/iam.admin` or `roles/iam.decisionViewer` (included in `roles/audit.viewer` and `roles/security.admin`) see more, each part only where they may read it: the rule that decided, the group path, the bindings, and every IAM change since, with who made it. Looking at someone else’s decision is itself recorded in the audit log.

Decisions are kept for 90 days. A decision younger than 15 minutes may not be logged yet and answers `DECISION_NOT_LOGGED_YET` with a retry delay. An unknown ID and an ID you may not see get the same `NOT_FOUND`. An agent’s remedy is to ask the person it acts for, with the decision ID.

## Quota and rate limits

Limits answer `RESOURCE_EXHAUSTED` (HTTP 429) in domain `quota.frazer.ai`, with reason `QUOTA_EXCEEDED`, `RATE_LIMITED`, `CONCURRENCY_LIMITED` or `BUDGET_CAPPED`. Load shedding answers `OVERLOADED`.

-   **Metadata**: `quotaMetric`, `quotaId`, `scope`, `location`, `limit`, `usage`, `reserved` and `requested`.
-   **`QuotaFailure`** repeats the numbers and, while an increase rolls out, the value it is moving to.
-   **`Help`** links to a pre-filled request for an increase.
-   **`RetryInfo`** comes only with rate and concurrency limits and overload: wait that long before you retry.

A plan fails with the same `QUOTA_EXCEEDED` as the change itself, so `--dry-run` and `terraform plan` show it before anything changes.

## Approvals

A change that needs a person’s approval is `FAILED_PRECONDITION` with reason `APPROVAL_REQUIRED` in domain `approvals.frazer.ai`. It is not a failure: nothing changed, and the change runs once a person approves it. Metadata: `approval`, `planHash`, `expireTime`, `rule` and `state`; `Help` links to the approval’s page, and the plan is attached. The CLI exits with code 13 (needs a person), or follows the approval with `--wait`. See [plans and approvals in the CLI](https://frazer.ai/docs/cli#plans-apply-and-approvals) and [writes through MCP](https://frazer.ai/docs/mcp#writes-become-plans).

## Read an error

### Console

An error shows in place of the section that failed: what failed, the request ID, **Retry** and **Ask Assistant**. An action’s error sits under its button or field, and a dialog keeps what you typed. Failed checks and error panels offer **Explain**. A page you may not see shows a lock, the role you need and **Ask an admin**.

### CLI

Errors go to stderr with the API’s message. A denial adds a `decision:` line and a `troubleshoot:` line; a quota error adds the limit, the usage and the link to request more. The exit code follows the table above.

### API

REST answers with the HTTP status and a JSON body, `{"error": {"code", "status", "message", "details"}}`, whose `details` hold the `ErrorInfo`, `QuotaFailure`, `RetryInfo` and `Help` entries. gRPC and Connect carry the same status and details. The [API reference](https://frazer.ai/docs/api#errors) shows a full denial.

### MCP

A failed call is a tool error (`isError`) with the reason, the domain and the metadata. Its `_meta` holds `frazer.ai/decisionId` and `frazer.ai/error`, the full error.

## Retry or fix

| Answer | What to do |
| --- | --- |
| `UNAVAILABLE`, `DEADLINE_EXCEEDED` | Retry with backoff. Retry a change only with the same `request_id` |
| `RATE_LIMITED`, `CONCURRENCY_LIMITED`, `OVERLOADED` | Wait for the `RetryInfo` delay, then retry |
| `ABORTED` (`ETAG_MISMATCH`, `PLAN_CHANGED`) | Read the resource again, plan again, then retry |
| `FAILED_PRECONDITION` | Change the state first: wait for the approval, undelete the project, remove the lien |
| `PERMISSION_DENIED`, `INVALID_ARGUMENT`, `NOT_FOUND`, `QUOTA_EXCEEDED` | Do not retry unchanged; fix the cause first |

> **Tip:**
> 
> Match on `domain` and `reason` together. A reason is unique within its domain, and both stay stable.

## Platform-wide reasons

Reasons any product can return: permissions, approvals, quotas, retries, projects and organizations.

| Reason | What it means | How to fix it |
| --- | --- | --- |
| `PERMISSION_DENIED` | You lack the permission the method needs on the resource, or the resource does not exist. Both get the same answer. | Ask for one of the roles in rolesGranting, or use Request access in the troubleshooter with the decision ID. |
| `ORG_RESTRICTED` | The token or MCP connection is bound to a different organization than the resource's. | Use a personal access token or MCP connection made for the resource's organization. |
| `STEP_UP_REQUIRED` | The method needs a passkey or second factor from the last 10 minutes. | Run frazer login --step-up, or confirm in the console, then retry. A personal access token never satisfies step-up. |
| `GRANT_NOT_HELD` | You tried to grant a role that contains a permission you do not hold yourself. | Ask someone who holds every permission of the role to grant it. The error names the role and the missing permission. |
| `APPROVAL_REQUIRED` | The change needs a person's approval before it runs. Nothing has changed yet. | Open the approval link, or follow it with --wait or frazer approvals wait. A person with approve rights decides in the console or the CLI. |
| `APPROVAL_PENDING` | You dismissed the approval link in your MCP client; the approval is still open. | Open the approval page in the console, or call the tool again to follow it. |
| `PLAN_CHANGED` | The target changed after the plan was made, so the plan no longer matches. | Plan the change again and have the new plan approved. |
| `ETAG_MISMATCH` | The resource changed since you read it. | Read it again, reapply your change and send the new etag. |
| `REQUEST_ID_REUSED` | A request ID came back with a different request. | Use a new request ID for a new request. Reuse an ID only to retry the identical call. |
| `QUOTA_EXCEEDED` | The change would take a quota counter past its limit. | Free capacity, or follow the Help link to request an increase. The error shows the limit, the usage and the amount requested. |
| `RATE_LIMITED` | Too many requests from this principal or organization. | Wait for the RetryInfo delay, then retry. |
| `CONCURRENCY_LIMITED` | Too many requests in flight at once. | Wait for the RetryInfo delay and send fewer requests in parallel. |
| `OVERLOADED` | Frazer is shedding load to protect the service. | Retry after the RetryInfo delay. |
| `BUDGET_CAPPED` | A capped budget is at 100 %, so new billable items in its scope are refused. Running workloads on your own servers are never stopped. | A billing admin or cost manager can lift or raise the cap, with a reason. Agents never can. |
| `DECISION_NOT_LOGGED_YET` | The decision is less than 15 minutes old and not in the decision log yet. | Retry after the RetryInfo delay. The CLI waits for you. |
| `READ_ONLY_EMPTY` | With Read-only on, the MCP client asked for nothing that read-only can grant. | Ask for read scopes in the client, or turn Read-only off for this connection if you mean to allow writes. |
| `LIEN_PRESENT` | A lien blocks deleting the project. The error names each lien and its reason. | Remove what the lien protects, such as servers in its pools, or delete the lien if you may, then delete the project. |
| `PROJECT_DELETE_REQUESTED` | The project is deleted and waiting to be purged, so it refuses changes. | Undelete the project within 30 days of its deletion to change it again. |
| `UNDELETE_WINDOW_PASSED` | The 30 days to undelete the project have passed. | Create a new project with a new ID. The old ID stays reserved. |
| `PROJECT_ID_UNAVAILABLE` | The project ID is taken, now or in the past. Project IDs are never reused. | Choose another project ID. |
| `ORGANIZATION_ID_UNAVAILABLE` | The organization ID is taken. | Choose another organization ID. |
| `CANCEL_NOT_SUPPORTED` | This operation cannot be canceled. | Wait for it to finish, then undo the change if you need to. |
| `FUNDS_EXHAUSTED` | Not run: no funds. The balance on the work's path (a budget, an agent's allocation, prepaid credits or a cap) cannot cover its estimate, so nothing started, and nothing runs a cheaper way instead. | Stop and follow next, which names the balance and the person task for a top-up. The work resumes on its own when funds arrive. Agents never pay. |
| `PERSON_TASK_REQUIRED` | A credential or another person task must be done before the call can run. | Follow next to the person task, tell the person what waits on it, and call again once it is done. |
| `PERSON_REQUIRED` | Only a person can give this answer, such as a decision or a reading to confirm. An agent never answers for them. | Follow next, which notifies the person or creates the person task. |
| `SESSION_REQUIRED` | An agent's commit, pull request version or push needs an open Fir session bound to that agent and workspace. Nothing was written. | Run the next call it prints (fir session open with the plan and item), or open\_session over MCP, then commit again. |
| `DUPLICATE_SUSPECTED` | With strict deduplication, a new issue scored as a duplicate of an existing one, so nothing was created. | Add an occurrence or a comment to the candidate, or call again with confirm\_new and the reason it is distinct. |
| `CONFIRMATION_REQUIRED` | A destructive command ran without a terminal and without --yes. Nothing changed. | Read the plan, then run the command again with --yes and --expect-plan with the plan's digest. |
| `TURN_LIMIT_REACHED` | A run stopped at the --max-turns you set, part way. Its best result so far is kept and printed (exit 14). | Keep the result, or run the next call it prints, which continues with a larger limit. |
| `STEP_LIMIT_REACHED` | A run stopped at the step limit you set, part way. Its best result so far is kept and printed (exit 14). | Keep the result, or run the next call it prints, which continues with a larger limit. |
| `BUDGET_REACHED` | A run spent the --budget you set and stopped at a turn boundary. Its best result so far is kept and printed (exit 14). | Keep the result, or continue with a larger --budget. Money that ran out is FUNDS\_EXHAUSTED instead. |
| `BUDGET_EXCEEDED` | The run's estimate is above the --budget you set, so it did not start (exit 5). Nothing changed. | Read the estimate, then run again with a larger --budget or a smaller job. |
| `INPUT_PLACEHOLDER` | An ID or value is a template, such as YOUR\_TOKEN or xxx, so the call was refused before it ran (exit 2). | Run the list call in next to find the real value. |
| `INPUT_TOO_LARGE` | More than 10 MB came on standard input with --input - (exit 2). | Write the request to a file and pass --input with the file. |
| `HOTFIX_CASE_NOT_MET` | An agent asked for hotfix priority, and its evidence does not resolve to a culprit revert, an incident fix or a fix for a red main@verified (exit 4). | The entry stays at its priority. On-call may set it by hand; next names them. |
| `HOTFIX_CAP_REACHED` | Agents in this repository used their 5 hotfix entries for the rolling hour. The entry is queued at normal and no person is asked (exit 6). | Nothing to do; it merges at normal. retry names when the hour rolls over. |

## Reasons by product

Each product’s own reasons, from its page.

### [Servers and pools](https://frazer.ai/docs/servers)

| Reason | What it means | How to fix it |
| --- | --- | --- |
| `GUARANTEE_EXCEEDS_CAPACITY` | The guarantees in a share add up to more than the pool's capacity minus one surge VM, or a guarantee is above its limit. | Lower a guarantee, set each limit at or above its guarantee, or add servers to the pool. |
| `POOL_CAPACITY_EXHAUSTED` | The placement would take the project past its limit, or the pool has no capacity left that no project holds. | Request more guaranteed capacity from the pool's admins (the plan offers it), place on the Frazer fleet where allowed, or add servers. |
| `POOL_NO_FIT` | The VMs do not fit on any server's free capacity with the pool's anti-affinity and placement mode. | Add servers to the pool, or plan fewer or smaller VMs. |
| `POOL_NOT_SHARED` | The pool is not shared with the project that tried to place on it. | Ask an admin of the pool's host project to add the project to the pool's share settings. |
| `POOL_SHARE_DRAINING` | The project's share of the pool is draining after an unshare and takes no new placements. | Place new work on another pool shared with the project, or on the Frazer fleet, and move running work before the deadline. |
| `POOL_IN_USE` | The pool still has consumer projects or servers. | Unshare the pool from every project and move or release its servers, then delete it. |

### [LLM gateway](https://frazer.ai/docs/llm-gateway)

| Reason | What it means | How to fix it |
| --- | --- | --- |
| `budget_exceeded` | HTTP 429. A budget on the key, the project or the organization is used up for the period, and its action is block. | Raise the budget (a plan) or wait for the next period. Check who spends with frazer llm usage --by key. |
| `rate_limited` | HTTP 429 with Retry-After. The key went over its requests per minute, tokens per minute or concurrency on the route. | Retry after the Retry-After time, or raise the key's limits. |
| `guardrail_blocked` | HTTP 400, naming the check. A guardrail check blocked the request, for example a secret in the prompt or a request above the cost limit. | Remove what the check found, or test the text against the policy in the Guardrails tab. Policy changes are made by a project admin. |
| `route_not_allowed` | HTTP 403. The key may not use this route, or the project does not allow direct provider/model calls. | Use a route the key lists, or add the route to the key. |
| `data_policy_violation` | HTTP 403. A step of the route could send data to a provider that the route's data policy does not allow. | Change the route's steps or its data policy. |
| `provider_unavailable` | HTTP 502, listing the steps tried. Every step of the route failed. | Check the provider's status and your key, and add a step that serves the same model from another provider. |
| `zdr_endpoint_denied` | HTTP 403. The project is in ZDR mode and the request needs something ZDR mode denies, such as a stateful provider feature. | Use a ZDR route and a stateless endpoint, or ask a project admin about the project's mode. |

### [Frazer Storage](https://frazer.ai/docs/storage)

| Reason | What it means | How to fix it |
| --- | --- | --- |
| `QuotaExceeded` | A write would take the project past its storage quota. S3 clients get it as HTTP 403. | Delete objects or old versions, add a lifecycle rule that expires them, or ask for a higher quota. |

### [Identity and access](https://frazer.ai/docs/iam)

| Reason | What it means | How to fix it |
| --- | --- | --- |
| `LAST_IAM_ADMIN` | The change would leave the organization with nobody who can change its IAM policy. | Grant the role to another person first. |
| `GROUP_CYCLE` | The group would contain itself through its members. | Remove the nesting that closes the loop. |
| `GROUP_DEPTH_EXCEEDED` | Groups would nest more than 5 levels deep. | Flatten the group tree. |
| `MEMBERSHIP_MANAGED_EXTERNALLY` | The membership comes from your identity provider through SCIM or a sign-in claim. | Change it in your identity provider. |
| `ETAG_REQUIRED` | A full policy replace was sent without the policy's etag. | Read the policy, edit it and send it back with its etag, or add and remove single bindings instead. |
| `LIMIT_EXCEEDED` | An IAM limit, such as bindings per resource, would be passed. | Use groups instead of many direct bindings, or ask for the limit to be raised. |
| `APPROVER_NOT_ALLOWED` | Only people can vote, and the approval's approvers must include you. | Ask an eligible approver; agents, service accounts and tokens never vote. |
| `APPROVER_CLIENT_NOT_ALLOWED` | Votes come only from the console or the Frazer CLI. | Vote in the console or with frazer approvals approve. |
| `SELF_APPROVAL` | You requested the change, or an agent asked for you, and the rule needs another person. | Ask a colleague who is an approver. |
| `APPLY_DEADLINE_EXCEEDED` | The approved change did not start within 15 minutes of the last vote. | Request the change again. |
| `PLAN_TOO_LARGE` | Plans over 256 KiB are not held for approval. | Split the change into smaller ones. |
| `BREAK_GLASS_NOT_ALLOWED` | Break-glass is only for a change you asked for directly, not through MCP, an agent or a workload. | Make the request yourself from the console or the CLI. |
| `HARDWARE_KEY_REQUIRED` | Break-glass needs a step-up with a hardware security key. | Step up with a device-bound security key. |

### [Billing and budgets](https://frazer.ai/docs/billing)

| Reason | What it means | How to fix it |
| --- | --- | --- |
| `BILLING_ACCOUNT_CLOSED` | The billing account is closed and cannot be linked. | Link the project to an active account. |
| `CURRENCY_NOT_SUPPORTED` | The account's currency is not supported. | Use USD. |
| `ACCOUNT_TYPE_NOT_SUPPORTED` | The requested billing account type is not available for this request. | Choose a self-serve or invoiced account. |
| `USAGE_DIGEST_NOT_SIGNED` | The hour's usage digest exists but is waiting to be signed. | Retry later. |
| `USAGE_DIGEST_MISSING` | A usage digest that should exist is gone. | Contact Frazer support with the project and the hour. |
| `QUOTA_BELOW_USAGE` | The requested limit is below current usage. | Reduce usage first, or pass ignore\_safety\_checks; nothing running is evicted. |

## Related

-   [API](https://frazer.ai/docs/api): the error model on every protocol
-   [CLI](https://frazer.ai/docs/cli#exit-codes): exit codes
-   [MCP](https://frazer.ai/docs/mcp): errors in tool results
-   [Identity and access](https://frazer.ai/docs/iam): roles, deny rules and approvals

## Pages in Reference

- [CLI](https://frazer.ai/docs/cli.md)
- [CLI commands](https://frazer.ai/docs/cli/commands.md)
- [Fir CLI](https://frazer.ai/docs/source/reference/cli.md)
- [API](https://frazer.ai/docs/api.md)
- [MCP](https://frazer.ai/docs/mcp.md)
- [Errors](https://frazer.ai/docs/errors.md) (this page)
- [Example values](https://frazer.ai/docs/example-values.md)
