Skip to content
prune.

Error codes

Why Prune blocked a call, and what to change.

Quick answer

When Prune blocks a call it returns the reason in the X-Prune-Shield-Code header and links to the matching entry below in X-Prune-Docs-Url and the docs_url field of the JSON body. A blocked call never reaches the provider, so it costs nothing. Errors from the provider itself (a bad model name, an invalid key) are passed through unchanged and have no Shield code.

What a blocked response looks like

A 429 with these headers: X-Prune-Shield-Code: spend_cap, X-Prune-Shield-Action: reject and X-Prune-Docs-Url: https://www.withprune.com/docs/errors#spend_cap. The body:

response.json
{
  "code": "spend_cap",
  "error": "Daily spend cap of $200.00 reached (current: $200.13).",
  "message": "Daily spend cap of $200.00 reached (current: $200.13).",
  "cap_usd": 200.0,
  "spent_usd": 200.13,
  "docs_url": "https://www.withprune.com/docs/errors#spend_cap"
}
handle_shield.py
from openai import OpenAI, APIStatusError

try:
    resp = client.chat.completions.create(model="gpt-4o", messages=messages)
except APIStatusError as e:
    code = e.response.headers.get("X-Prune-Shield-Code")
    if code == "customer_budget":
        show_upgrade_prompt()
    elif e.status_code == 503 and code:
        retry_later()
    else:
        print(code, e.response.headers.get("X-Prune-Docs-Url"))
        raise

Spend limits

Shield stopped the call before it reached the provider, so it cost nothing.

spend_cap HTTP 429

This API key reached its daily spend cap in Shield. The count resets at 00:00 UTC. Cache hits do not count.

Fix: Open Shield in the dashboard and raise the cap or switch mode. Check Activity first to see what spent it.

anomaly_spike HTTP 429

Spend on this key jumped far above its normal rate (Balanced mode blocks at 8x).

Fix: If the spike is expected (a launch, a batch job), raise the multiplier or turn spike detection off for that key in Shield.

token_budget HTTP 429

This key used up the daily token budget set for this model.

Fix: Raise the per-model token budget in Shield, or send the work to a cheaper model.

customer_budget HTTP 429

The customer in X-Prune-Customer-Id crossed their monthly budget. The response includes spend, budget and period. Budgets reset on the 1st (UTC).

Fix: Raise the budget on the Customers page, or show your user an upgrade prompt. This is the code to catch for per-user limits.

rate_limit HTTP 429

Too many requests per minute on this key (Balanced allows 300, Locked down 60).

Fix: Back off and retry, or raise the rate limit in Shield.

plan_limit HTTP 402

Your Prune plan's monthly request quota is used up.

Fix: Upgrade on the Billing page. Quotas reset monthly.

free_trial_ended HTTP 402

The free trial on this account has ended.

Fix: Pick a plan on the Billing page.

Agent guards

Limits on a single agent run. Send X-Prune-Run-Id on every call of the run so they apply.

run_budget HTTP 429

This run has spent the amount in X-Prune-Run-Budget-Usd.

Fix: Look at the run in Activity. If it was a real task, raise the budget. If it was looping, fix the tool it kept calling.

run_budget_missing_run_id HTTP 400

X-Prune-Run-Budget-Usd was sent without X-Prune-Run-Id, so there is no run to count against.

Fix: Send one X-Prune-Run-Id per agent run on every call of that run.

agent_step_budget HTTP 429

The run made more calls than X-Prune-Max-Steps (default 50 for tool-using calls).

Fix: Raise X-Prune-Max-Steps if the task really needs more steps (up to 500). Otherwise the agent is stuck.

agent_loop HTTP 429

The same step repeated three times in one run.

Fix: Fix the tool or prompt the agent is retrying. Raising limits will not help here.

loop_detected HTTP 429

Too many near-identical requests in a short window (default 100 in 60 seconds), even without a run ID.

Fix: Find the retry loop in your code. Raise the threshold in Shield only if the traffic is intended.

Access rules

Allowlists and blocks you set in Shield.

ip_blocked HTTP 403

The request came from an IP that is not on this key's allowlist.

Fix: Add the server's IP in Shield, or remove the allowlist.

provider_blocked HTTP 403

This key is not allowed to call this provider.

Fix: Add the provider to the key's allowed providers in Shield.

model_blocked HTTP 403

This key is not allowed to call this model.

Fix: Add the model to the key's allowed models in Shield, or call an allowed one.

tool_policy HTTP 403

The request asked the model to call a tool your policy denies.

Fix: Check the tool allow and deny lists in Shield. Keep dangerous tools behind a human approval.

Content checks

Checks on what goes into and comes out of the model.

prompt_injection HTTP 403

The input looked like a prompt injection and you opted into blocking with X-Prune-Injection: block.

Fix: Review the input in Activity. Use detect mode (the default) if you only want a signal.

prompt_injection_signal HTTP 200

Not a block. The input looked like a prompt injection and the call went through (detect mode).

Fix: Log it on your side, or switch to block mode for this route.

canary_leak HTTP 403

The canary token Prune added to your system prompt appeared in the output, so the model was leaking its instructions.

Fix: Treat the input as an attack. Turn off X-Prune-Shield-Canary-Block if you only want to log leaks.

Voice

Limits on voice calls and text to speech.

call_budget HTTP 429

The call spent the amount in X-Prune-Call-Budget-Usd.

Fix: Raise the call budget or end long calls sooner.

call_duration HTTP 429

The call ran longer than X-Prune-Call-Max-Seconds.

Fix: Raise the limit if long calls are expected.

repeat_loop HTTP 429

The voice agent said the same line too many times.

Fix: Fix the step the agent keeps repeating.

card_readback_blocked HTTP 422

Text to speech refused to read a card number aloud.

Fix: Mask the number, or send X-Prune-Allow-Card-Readback: true if you really need it.

Prune could not check

Prune fails closed: if it cannot check a limit, it rejects the call instead of letting it through unguarded. These are safe to retry.

shield_unavailable HTTP 503

Prune could not load your Shield policy or today's spend.

Fix: Retry with backoff. The Retry-After header says how long to wait.

shield_preflight_timeout HTTP 503

A Shield check took too long.

Fix: Retry shortly.

shield_preflight_unavailable HTTP 503

A Shield check could not run.

Fix: Retry shortly.

check_failed HTTP 503

Prune could not verify your plan usage.

Fix: Retry shortly.

Related

Have an account? Sign in · PyPI.