# Errors

Errors are [RFC 7807](https://www.rfc-editor.org/rfc/rfc7807) `application/problem+json` responses. The `type` field is a URL of the code's entry below (`https://wherebox.dev/docs/errors#<code>`, or under the server's `WHEREBOX_DOCS_URL`), so clients can branch on the code after `#`.

Every problem also carries:

- `requestId`: Id of the request (same as the `X-Request-Id` header). Quote it when reporting a problem.

<a id="invalid-spec"></a>
## `invalid-spec`

**400** · Invalid map spec

A parameter or JSON field is missing, malformed or out of range — including unknown query parameters (with a "did you mean" hint).

Extra fields:

- `issues`: `[{path, message}]` — one entry per problem.

```json
{
  "type": "https://wherebox.dev/docs/errors#invalid-spec",
  "title": "Invalid map spec",
  "status": 400,
  "detail": "The request does not match the map spec",
  "issues": [
    {
      "path": "width",
      "message": "Too big: expected number to be <=2048"
    },
    {
      "path": "fadecolour",
      "message": "unknown parameter \"fadecolour\" (did you mean \"fadecolor\"?)"
    }
  ],
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="bad-request"></a>
## `bad-request`

**400** · Bad request

The request cannot be processed as sent: an unsupported image extension, an invalid image key, a missing search query, a rejected icon or font, a provider/routing key that is too long, routing that is not configured on this server, or a `worldview` whose data is not imported on this server.

```json
{
  "type": "https://wherebox.dev/docs/errors#bad-request",
  "title": "Bad request",
  "status": 400,
  "detail": "routing is not configured on this server",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="not-found"></a>
## `not-found`

**404** · Not found

No area or place matches the name, or a cached image expired.

Extra fields:

- `suggestions`: Close matches, when there are any.

```json
{
  "type": "https://wherebox.dev/docs/errors#not-found",
  "title": "Not found",
  "status": 404,
  "detail": "no area named \"Prgaue\"",
  "suggestions": [
    {
      "id": "r435514",
      "ref": "osm:r435514",
      "name": "Praha",
      "adminLevel": 4,
      "context": "Česko"
    }
  ],
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="ambiguous"></a>
## `ambiguous`

**409** · Ambiguous name

Several areas match the name. Pick one by id (`osm:r…`) or narrow with `level`/`country`.

Extra fields:

- `candidates`: Matching areas with `ref` (pass it as `area`, e.g. `osm:r439539`), `adminLevel` and `context` (e.g. "Česko › Liberecký kraj").

```json
{
  "type": "https://wherebox.dev/docs/errors#ambiguous",
  "title": "Ambiguous name",
  "status": 409,
  "detail": "\"Nová Ves\" matches 10 areas; pass level, country or an id",
  "candidates": [
    {
      "id": "r439539",
      "ref": "osm:r439539",
      "name": "Nová Ves",
      "adminLevel": 8,
      "context": "Česko › Karlovarský kraj › okres Sokolov"
    },
    {
      "id": "r439063",
      "ref": "osm:r439063",
      "name": "Nová Ves",
      "adminLevel": 8,
      "context": "Česko › Liberecký kraj › okres Liberec"
    }
  ],
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="unauthorized"></a>
## `unauthorized`

**401** · Unauthorized

No API key where one is needed, or the key is unknown. Without any key, a server may offer a small demo allowance for GET maps and searches (`WHEREBOX_ANONYMOUS`); everything else, such as `POST /v1/map`, needs a key, and a key that is present but malformed or unknown is always refused. Send `Authorization: Bearer sk_…` (server-to-server) or a signed URL with `apikey=pk_…&sig=…` (embeds). Apps connected with OAuth get this when their access token is expired, revoked or issued for another resource; they should refresh it.

```json
{
  "type": "https://wherebox.dev/docs/errors#unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "missing API key",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="forbidden"></a>
## `forbidden`

**403** · Forbidden

The key is revoked, the account is disabled, the URL signature is wrong or expired, the origin is not allowed for this key, or a secret key was put in a URL.

```json
{
  "type": "https://wherebox.dev/docs/errors#forbidden",
  "title": "Forbidden",
  "status": 403,
  "detail": "invalid URL signature",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="plan-limit"></a>
## `plan-limit`

**403** · Not included in your plan

The request needs something the key's plan (or the anonymous demo allowance, for requests without a key) does not include, such as a larger image or server-side routing.

```json
{
  "type": "https://wherebox.dev/docs/errors#plan-limit",
  "title": "Not included in your plan",
  "status": 403,
  "detail": "image is 4.2 MP; the free plan allows up to 1 MP",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="rate-limited"></a>
## `rate-limited`

**429** · Too many requests

The account (or, without a key, the client network on the demo allowance) exceeded its requests per minute. Wait `Retry-After` seconds; see the `X-RateLimit-*` headers.

```json
{
  "type": "https://wherebox.dev/docs/errors#rate-limited",
  "title": "Too many requests",
  "status": 429,
  "detail": "rate limit of 30 requests/minute exceeded",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="quota-exceeded"></a>
## `quota-exceeded`

**429** · Quota exceeded

The account used all renders of its plan this month (UTC). Cached images keep working; new renders resume next month (UTC) or once the limit is raised. The `detail` says which allowance (all renders or routed renders) is used up. Without a key, the demo allowance is per day: per client network and for all anonymous visitors together, reset at midnight UTC; a free key lifts it.

```json
{
  "type": "https://wherebox.dev/docs/errors#quota-exceeded",
  "title": "Quota exceeded",
  "status": 429,
  "detail": "monthly quota of 1000 renders used",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="no-route"></a>
## `no-route`

**422** · No route found

The routing provider found no route between the points for this profile.

```json
{
  "type": "https://wherebox.dev/docs/errors#no-route",
  "title": "No route found",
  "status": 422,
  "detail": "no route found between the points for profile \"foot\"",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="busy"></a>
## `busy`

**503** · Busy

The render queue is full. Retry after the `Retry-After` seconds.

```json
{
  "type": "https://wherebox.dev/docs/errors#busy",
  "title": "Busy",
  "status": 503,
  "detail": "renderer is busy, retry shortly",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="upstream-failed"></a>
## `upstream-failed`

**502** · Upstream failure

A basemap, tile, routing, geocoder or asset server failed or returned something unusable.

Extra fields:

- `detail`: What failed (secrets removed).

```json
{
  "type": "https://wherebox.dev/docs/errors#upstream-failed",
  "title": "Upstream failure",
  "status": 502,
  "detail": "GET https://tiles.example.com/style.json -> 500",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```

<a id="upstream-timeout"></a>
## `upstream-timeout`

**504** · Upstream timeout

A routing, geocoder or asset server did not answer in time.

```json
{
  "type": "https://wherebox.dev/docs/errors#upstream-timeout",
  "title": "Upstream timeout",
  "status": 504,
  "detail": "routing provider did not answer within 10000 ms",
  "requestId": "3f2c9a1e-7b1d-4c0e-9a55-0d2f6e8b1c44"
}
```
