> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opentype.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# deadline_exceeded

> HTTP 504 on POST /v1/runs: the run's deadline passed before it finished. Raise deadline_ms (up to 150,000) or shrink the input, then retry with a new key.

`deadline_exceeded` means a run did not finish within its deadline. Read this page when runs time out, to choose a better `deadline_ms` and to retry safely.

| HTTP  | `code`              | Retryable                                                                         |
| ----- | ------------------- | --------------------------------------------------------------------------------- |
| `504` | `deadline_exceeded` | Yes, with a longer `deadline_ms` or a smaller input, and a new `Idempotency-Key`. |

## What happened

Route: `POST /v1/runs`.

Every run has a deadline, set with `deadline_ms`:

| `deadline_ms`         | Value                                                                                                        |
| --------------------- | ------------------------------------------------------------------------------------------------------------ |
| Default, decision run | 30,000 plus 120,000 per 262,144 input tokens: about 30 seconds for a small state, 90 seconds for 128k tokens |
| Range, decision run   | 1 to 150,000; values outside are clamped, so `0` becomes `1` and `300000` becomes `150000`                   |
| Default, verdict run  | `30000` (30 seconds)                                                                                         |
| Range, verdict run    | 1 to 120,000                                                                                                 |

The deadline covers choosing a model and the model call. When it passes in either step, the run fails with this code.

The message always reads "the request deadline passed before a route was chosen", even when the model call is what timed out. Do not use the message to tell the two apart.

The run was admitted, then failed before the model served it. Your organization is not charged: the run's hold is released. The run itself is not moved out of `pending`, and it stays `pending`. It is listed by `GET /v1/runs` and counted under `in_flight` in `GET /v1/usage`.

Because of that, a replay with the **same** `Idempotency-Key` returns `202` with `"state": "pending"`, never the answer. Retry with a **new** key.

## How to fix

1. **Raise `deadline_ms`,** up to `120000`. Set your HTTP client's own timeout above it, or your client gives up first and you never see the answer.
2. **Shrink the work.** Trim the `state`, lower `think_tokens`, lower `draws`, or split a large question set across runs.
3. **Retry with a new `Idempotency-Key`.** `deadline_ms` is not part of the key's identity, so the old key replays the pending run (`202`) instead of running with the new deadline.
4. If runs time out at the maximum deadline with a small input, retry later with backoff and report the `request_id`.

## Example

```json theme={"system"}
{"error":{"code":"deadline_exceeded","message":"the request deadline passed before a route was chosen","request_id":"req_7d3f0c1a9b2e4f6a8c0d1e2f3a4b5c6d"}}
```

A retry with a longer deadline and a new key per attempt. Keep the client timeout above `deadline_ms`:

<CodeGroup>
  ```bash curl theme={"system"}
  BODY='{"kind":"decision","state":{"ticket":"I was charged twice this month and nobody answers my emails."},"questions":{"urgent":{"type":"noul","instructions":"reply within the hour?"}},"max_output_tokens":16,"deadline_ms":60000}'

  for attempt in 1 2 3 4; do
    # A new Idempotency-Key per attempt: the failed run stays pending under the old key.
    RESP=$(curl -sS -w '\n%{http_code}' https://api.opentype.dev/v1/runs \
      -H "Authorization: Bearer $OPENTYPE_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d "$BODY")
    STATUS=$(printf '%s' "$RESP" | tail -n1)
    printf '%s\n' "$RESP" | sed '$d'
    case "$STATUS" in
      5??) sleep $((2 ** attempt)) ;;  # 500, 503, 504: back off, then try again
      *) break ;;                        # success or 4xx: stop
    esac
  done
  ```

  ```typescript TypeScript theme={"system"}
  const body = {"kind":"decision","state":{"ticket":"I was charged twice this month and nobody answers my emails."},"questions":{"urgent":{"type":"noul","instructions":"reply within the hour?"}},"max_output_tokens":16,"deadline_ms":60000};

  async function createRunWithRetry(payload: unknown, maxAttempts = 4) {
    for (let attempt = 1; ; attempt++) {
      const res = await fetch("https://api.opentype.dev/v1/runs", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.OPENTYPE_API_KEY}`,
          "Content-Type": "application/json",
          // A new key per attempt: the failed run stays pending under the old key.
          "Idempotency-Key": crypto.randomUUID(),
        },
        body: JSON.stringify(payload),
      });
      const data = await res.json();
      if (res.ok) return data;

      const { code, message, request_id } = data.error;
      console.error(`POST /v1/runs -> ${res.status} ${code}: ${message} (${request_id})`);
      if (res.status < 500 || attempt >= maxAttempts) throw new Error(`${code} (${request_id})`);
      await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
    }
  }

  const run = await createRunWithRetry(body);
  ```

  ```python Python theme={"system"}
  import os
  import time
  import uuid

  import requests

  body = {"kind":"decision","state":{"ticket":"I was charged twice this month and nobody answers my emails."},"questions":{"urgent":{"type":"noul","instructions":"reply within the hour?"}},"max_output_tokens":16,"deadline_ms":60000}

  def create_run_with_retry(payload: dict, max_attempts: int = 4) -> dict:
      for attempt in range(1, max_attempts + 1):
          resp = requests.post(
              "https://api.opentype.dev/v1/runs",
              headers={
                  "Authorization": f"Bearer {os.environ['OPENTYPE_API_KEY']}",
                  # A new key per attempt: the failed run stays pending under the old key.
                  "Idempotency-Key": str(uuid.uuid4()),
              },
              json=payload,
              timeout=160,  # longer than the largest deadline (150,000 ms)
          )
          data = resp.json()
          if resp.ok:
              return data

          err = data["error"]
          print(f"POST /v1/runs -> {resp.status_code} {err['code']}: {err['message']} ({err['request_id']})")
          if resp.status_code < 500 or attempt == max_attempts:
              raise RuntimeError(f"{err['code']} ({err['request_id']})")
          time.sleep(2 ** attempt)

  run = create_run_with_retry(body)
  ```
</CodeGroup>

## Related

* [Limits](/reference/limits) - `deadline_ms` and every other bound.
* [Polling](/guides/polling) - read a run later with `GET /v1/runs/{run_id}`.
* [Idempotency](/guides/idempotency) - when to reuse an `Idempotency-Key` and when to send a new one.
* [Error handling](/guides/error-handling) - a status-to-action table and a retry helper for every error.
* [Problem codes](/problems) - every code, its status, and whether a retry can help.
