# Error codes and cost reporting

Every refused or failed tool call says why with a stable **code** and a **retry hint**, and every tool result says what it cost. Agents can branch on the code instead of parsing English.

## How an error looks

A tool result that is JSON keeps its `error` field and gains `error_info`:

```json
{
  "error": "pull_ceiling_exceeded",
  "message": "This account has reached its fair-use cap ...",
  "error_info": {
    "code": "pull_ceiling_exceeded",
    "message": "This account has reached its fair-use cap ...",
    "retryable": false,
    "retry_after_s": null,
    "docs": "https://justclaw.it.com/docs/errors#pull_ceiling_exceeded",
    "legacy_error_until": "2027-02-03"
  }
}
```

Refusals that arrive as plain text (quota and keyless limits, answered before the tool runs) carry the same code in the result's `_meta["com.justclaw-it/call"].error_code`.

A refusal is still a normal tool result (`isError` is false, as before): check for `error` / `error_info` in the JSON. `isError` is true only when the tool itself failed unexpectedly (`internal_error`).

Treat a code you don't know like `internal_error` and follow its `retry_after_s`. A `null` `retry_after_s` means retrying the same call will not help.

## Compatibility (90 days)

The old `error` values stay exactly as they were until **2027-02-03**. After that date `error` holds the stable code itself. Most errors already do; the changes are `not available` → `not_available`, `not found` → `not_found`, `unknown tool …` → `unknown_tool`, and free-text argument errors → `invalid_argument` (the text moves to `error_info.message`). Read `error_info.code` today and nothing changes for you.

## Cost reporting

Every tool result carries `_meta["com.justclaw-it/call"]`:

| field | meaning |
| --- | --- |
| `request_id` | id of this call, also sent as the `X-Request-ID` response header; quote it to support |
| `credits_charged` | credits this call took (0 for free tools) |
| `credits_refunded` | credits given back because the call was refused or found nothing |
| `balance` | the account's spendable credits after this call (keyed and signed-in callers) |

JSON-RPC batch requests (several messages in one POST) are refused with `batch_not_supported` before anything is charged; send one message per request.

## Account and plan

#### `key_required`

This tool needs an API key (free, no card). Keyless use covers search_components only. Retrying the same call won't help: fix the call, change plan or wait.

#### `pro_required`

This tool or option is part of the Pro plan. Retrying the same call won't help: fix the call, change plan or wait.

#### `out_of_credits`

No credits left for this period. Wait for the refill or upgrade. Retry after the next credit refill (or upgrade).

#### `ip_daily_free_cap`

The free plan's daily credit cap for your network is used up. Retry after 00:00 UTC (`retry_after_s` counts down to it).

#### `revoked`

This API key was revoked. Create a new one on /account. Retrying the same call won't help: fix the call, change plan or wait.

## Rate limits and caps

#### `search_rate_limited`

Too many free discovery calls in the last minute. Retry after 60 seconds.

#### `search_daily_cap`

Daily free discovery limit reached for this key or network. Retry after 00:00 UTC (`retry_after_s` counts down to it).

#### `too_many_requests`

Too many requests from your network. Retry after 60 seconds.

#### `unavailable_pulls_rate_limited`

Too many get_component calls for ids that are not available. Retry after 60 seconds.

#### `pull_ceiling_exceeded`

Fair-use cap on distinct components pulled in 12 months reached. Retrying the same call won't help: fix the call, change plan or wait.

#### `daily_limit`

Daily limit for this tool reached. Retry after 00:00 UTC (`retry_after_s` counts down to it).

#### `build_limit`

Daily build limit reached. Retry after 00:00 UTC (`retry_after_s` counts down to it).

#### `daily_ai_budget`

The service's daily AI budget for this tool is used up. Retry after 00:00 UTC (`retry_after_s` counts down to it).

#### `report_daily_limit`

Daily licence report limit for this account reached. Retry after 00:00 UTC (`retry_after_s` counts down to it).

## Problems with the request

#### `not_available`

No component with that id is available (missing, or not licence-cleared). Retrying the same call won't help: fix the call, change plan or wait.

#### `not_found`

Nothing found for that id. Retrying the same call won't help: fix the call, change plan or wait.

#### `invalid_argument`

An argument has a value the tool does not accept; the message says which. Retrying the same call won't help: fix the call, change plan or wait.

#### `bad_request`

The request is malformed. Retrying the same call won't help: fix the call, change plan or wait.

#### `bad_prompt`

The prompt was empty or not usable. Retrying the same call won't help: fix the call, change plan or wait.

#### `too_many`

Too many items in one call; split it. Retrying the same call won't help: fix the call, change plan or wait.

#### `nothing_to_check`

The call named nothing to check. Retrying the same call won't help: fix the call, change plan or wait.

#### `no_components`

No components matched; nothing was built (refunded). Retrying the same call won't help: fix the call, change plan or wait.

#### `unknown_tool`

No tool with that name. Retrying the same call won't help: fix the call, change plan or wait.

#### `batch_not_supported`

JSON-RPC batch requests are not supported; send one message per POST. Retrying the same call won't help: fix the call, change plan or wait.

#### `feature_not_enabled`

This feature is not enabled for this account yet. Retrying the same call won't help: fix the call, change plan or wait.

#### `file_too_large`

An uploaded file is larger than the limit; send a smaller file. Retrying the same call won't help: fix the call, change plan or wait.

#### `too_many_files`

Too many files in one report. Retrying the same call won't help: fix the call, change plan or wait.

#### `too_many_packages`

The files list more packages than one report allows; split them. Retrying the same call won't help: fix the call, change plan or wait.

#### `unsupported_lockfile`

That file type is not supported; the message lists the supported ones. Retrying the same call won't help: fix the call, change plan or wait.

#### `parse_failed`

The file could not be parsed (refunded). Retrying the same call won't help: fix the call, change plan or wait.

#### `parse_timeout`

Parsing took too long; send a smaller file (refunded). Retrying the same call won't help: fix the call, change plan or wait.

#### `empty_report`

No packages were found in the files (refunded). Retrying the same call won't help: fix the call, change plan or wait.

## Temporary service problems

#### `registry_busy`

The registry is busy; retry shortly. Retry after 5 seconds.

#### `builder_unavailable`

The builder is temporarily unavailable. Retry after 30 seconds.

#### `adapt_failed`

Adapting the components failed; retry or change the prompt. Retry after 30 seconds.

#### `worker_response_too_large`

The result was too large to return. Retrying the same call won't help: fix the call, change plan or wait.

#### `lost`

The build result expired or was lost; start a new build. Retrying the same call won't help: fix the call, change plan or wait.

#### `internal_error`

Unexpected server error. Retry once; if it repeats, contact support with the request_id. Retry after 5 seconds.
