# Troubleshooting

> Every error the OpenWAF Egress gateway returns (401, 403, 404, 413, 502 and more), what causes it and how to fix it.

Every error the gateway itself returns is JSON with an `error` code and a `message`. Errors from the provider are passed through unchanged; the gateway's own always carry one of the codes below. The gateway also logs each refusal on standard output (`docker logs openwaf-egress`).

```json
{"error": "egress_blocked", "message": "Agent \"my-support-agent\" is not permitted to access target \"openai\""}
```

> **Note: Hosted gateway**
>
> On `egress.openwaf.dev` a few answers differ (an agent is identified by its token, a target without a key is refused, `503 policy_unavailable`): see the [hosted egress reference](https://developers.openwaf.dev/egress/cloud/reference/#gateway-errors).


## All error codes

| Status | `error` | Cause |
|---|---|---|
| 400 | `body_read_error` | The request body could not be read (client disconnected or broken transfer). |
| 401 | `unauthorized_agent` | No agent matches: the name in the URL or in `X-OpenWAF-Agent` is not in `agents`, or on `/v1/proxy/…` no agent has the token sent. |
| 401 | `token_required` | The agent has a `token` and the request has no `Authorization` header. |
| 401 | `invalid_token` | The `Authorization: Bearer` value is not the agent's `token`. |
| 403 | `egress_blocked` | The target exists but is not in the agent's `allowed_targets`. |
| 403 | `ssrf_blocked` | The target's `host` resolves to a private or reserved address, or does not resolve. |
| 403 | `injection_detected` | A prompt-injection detector matched the body (`enforce_injection`). |
| 403 | `data_leak_blocked` | A data-leak pattern matched the body (`block_dlp`). |
| 404 | `invalid_route` | The path is not `/v1/agent/<agent>/proxy/<target>/…`, `/v1/proxy/<target>/…` or `/health`. |
| 404 | `unknown_target` | The target alias is not in `targets`. |
| 413 | `body_too_large` | The body is over 8 MiB, or, with inspection on, its text outside inline media is over 1 MiB. |
| 500 | `invalid_upstream_url`, `request_creation_failed` | The target's `base_url` cannot be parsed, or the outgoing request cannot be built. |
| 502 | `credential_resolution_failed` | The key could not be read from OpenBao or Vault. |
| 502 | `upstream_error` | The provider could not be reached (DNS, connection, TLS or timeout). |

## 401: the agent is not identified

- `unauthorized_agent`: check the spelling of `<agent>` in the base URL against the keys of `agents`. Names are case-sensitive.
- `token_required`: the SDK sends the key in `Authorization: Bearer`. If it sends it in another header (for example `x-api-key`), the gateway does not see it.
- `invalid_token`: the placeholder in the agent does not match `token` in the configuration. Remember that a restart is needed after editing the file.

## 403: the request was refused

**`egress_blocked`**: add the target alias to the agent's `allowed_targets`.

**`ssrf_blocked`**: the message says which address was refused, for example `destination IP 169.254.169.254 is private or reserved`. A provider hostname that resolves to a private address on your network (split DNS, a local proxy) is refused too. A resolution error also lands here: check DNS from inside the container.

**`injection_detected`**: the response lists each hit with its detector `ID`, `Label` and the matched `Snippet`:

```json
{"agent":"my-support-agent","detections":[{"ID":"pi.override.en","Label":"Instruction override","Snippet":"Ignore all previous instructions"}],"error":"injection_detected","message":"Request body contains prompt injection or adversarial pattern","target":"openrouter"}
```

Look at the snippet: it is the text that matched, at most 160 bytes. If it came from a document or a web page the agent read, the agent is doing its job by not acting on it. Detectors cannot be turned off one by one in v0.1.1; for an agent that must process such text, set `enforce_injection` to `false` for that agent only.

**`data_leak_blocked`**: the response lists each `type` (`credit_card`, `private_key`, `api_key`) with a description. Card numbers are shown with the first and last four digits only. Remove the data from the prompt, or set `block_dlp` to `false` for that agent.

## 413: the body is too large

The gateway never cuts a body and never forwards part of it uninspected. With `enforce_injection` or `block_dlp` on, the text must fit 1 MiB. Inline media does not count: `data:image/…;base64,…` URIs (audio and video too) and JSON `"data"` members of at least 1 KiB of bare base64 (OpenAI `input_audio`, Gemini `inlineData`, Anthropic base64 sources). Send large files by URL or through the provider's file API, or split the request. No body may exceed 8 MiB.

## 502: OpenBao or the provider failed

**`credential_resolution_failed`**: the log line says which secret failed, without its value:

```text
[EGRESS] Failed resolving secret agents/support:OPENROUTER_API_KEY for agent my-support-agent: vault error 403 reading agents/support
```

- `obtaining vault token: approle login failed with status …`: wrong `role_id` or `secret_id`, or the secret ID expired.
- `vault error 403`: the AppRole policy does not allow `read` on `<kv_mount>/data/<path>`, often because `kv_mount` or `path` is wrong. With KV v2 the mount is not part of `path`.
- `vault error 404`: there is no secret at that path.
- `key "…" not found in path "…"`: the secret exists but has no field with that name.
- A connection or certificate error: check `addr` and `ca_file`, and that `/health` reports `"openbao": true`.

**`upstream_error`**: the message includes the error. Check the target's `base_url`, DNS and outbound access from the container. The gateway waits up to 5 minutes for the provider's response headers and 15 minutes for a whole exchange, streamed body included.

## The provider answers 401 or 403

The error comes from the provider, passed through. Usual causes:

- The target has no entry in the agent's `credential_mapping`, so the request reached the provider without a key.
- The key in OpenBao is wrong, revoked or rotated less than five minutes ago (the gateway caches secrets for five minutes).
- The target's `auth_header` or `auth_prefix` does not match what the provider expects (Anthropic: `x-api-key` with no prefix; Gemini: `x-goog-api-key`). Keys in the query string (`?key=`) are not injected.

## The agent gets a 3xx

The gateway does not follow redirects; the provider's `3xx` reaches the agent as it is. Point `base_url` at the final URL the provider documents (for example `https://` instead of `http://`).

## /health says "openbao": false

Either `openbao.addr` is not set, or the gateway could not get `200` or `429` from `<addr>/v1/sys/health` within three seconds: the server is sealed or unreachable, or its certificate is not trusted (set `ca_file`). Requests to targets with a `credential_mapping` will fail with `502` until it is fixed.

Written from [github.com/openwaf-dev/egress@v0.1.1](https://github.com/openwaf-dev/egress/tree/v0.1.1).
