# Errors: TokenPoliceBlockedError (/docs/sdk/errors)



TokenPolice is fail-open by design: if anything inside the SDK or the collector goes wrong, your
LLM call still runs. There is exactly **one** exception it will ever raise into your code —
`TokenPoliceBlockedError` — and only on a deliberate block.

## When it fires [#when-it-fires]

Only when **both dials say enforce**: the SDK is `firewall="enforce"` **and** the rule that matched
is set to **Enforce**, and that rule's decision is to block. In every other case — dry-run on
either dial, a check timeout, a collector outage — nothing is raised and the call proceeds.

See [Dry-run vs enforce](/docs/concepts/dry-run-vs-enforce) for the full truth table.

## Catching it [#catching-it]

Wrap the call and read the structured fields.

```python
import token_police as tp

try:
    resp = client.chat.completions.create(...)
except tp.TokenPoliceBlockedError as e:
    print(e.reason)     # human-readable reason
    print(e.rule_id)    # which rule blocked
    print(e.kind)       # what kind of block (e.g. "budget", a loop detector)
    print(e.trace_id)   # the trace this decision belongs to
```

```typescript
import * as tp from "token-police";

try {
  const resp = await client.chat.completions.create(/* ... */);
} catch (e) {
  if (e instanceof tp.TokenPoliceBlockedError) {
    console.log(e.reason, e.ruleId, e.kind, e.traceId);
  }
}
```

## The fields [#the-fields]

| Python     | Node      | What it is                                                     |
| ---------- | --------- | -------------------------------------------------------------- |
| `reason`   | `reason`  | Human-readable reason for the block.                           |
| `rule_id`  | `ruleId`  | The rule that produced the decision.                           |
| `kind`     | `kind`    | The block category — e.g. `"budget"`, or a loop-detector name. |
| `trace_id` | `traceId` | The trace this run belongs to.                                 |

<Callout type="info">
  This is the only error to catch. Everything else about TokenPolice is fail-open — a network
  blip, a bad response, the collector being down: your call goes through untouched. Handling
  `TokenPoliceBlockedError` is all the error handling you need.
</Callout>

## Next [#next]

<Cards>
  <Card title="Fail-open" href="/docs/concepts/fail-open" description="Why every other failure lets your call through." />

  <Card title="Firewall rules" href="/docs/rules/overview" description="The rules that decide when to block." />
</Cards>
