# Problems

---

# Problem Details

_Wispist / Problems_

> Wispist errors use RFC 9457 Problem Details with permanent type URIs, request-correlated instances, stable titles, and ergonomic browser codes.

Wispist API errors use RFC 9457 Problem Details and `application/problem+json`:

```json
{
  "type": "https://learn.peios.org/wispist/problems/revision-conflict/",
  "title": "Revision conflict",
  "status": 412,
  "detail": "The document changed after it was read.",
  "instance": "urn:uuid:019c0000-0000-7000-8000-000000000000"
}
```

The type URI is the primary machine identifier. It is permanent even if the implementation or documentation later moves. `title` is stable for that type; `detail` describes this occurrence. `status` matches the HTTP status.

`instance` is a `urn:uuid:` URI built from the same UUID returned in `X-Request-ID`. Give that request ID to an operator when investigating a server-side failure.

Validation problems may add an `errors` array of JSON Pointer and detail objects. Internal SQL, paths, stack traces, document bodies, tokens, and policy predicates are never returned.

The browser client maps known type URIs to convenient `error.code` values:

| HTTP | Type | Browser code |
| ---: | --- | --- |
| 400 | [Invalid request](/wispist/problems/invalid-request.md) | `invalid_request` |
| 400 | [Invalid JSON](/wispist/problems/invalid-json.md) | `invalid_json` |
| 401 | [Authentication required](/wispist/problems/authentication-required.md) | `authentication_required` |
| 403 | [Forbidden](/wispist/problems/forbidden.md) | `forbidden` |
| 404 | [Not found](/wispist/problems/not-found.md) | `not_found` |
| 405 | [Method not allowed](/wispist/problems/method-not-allowed.md) | `method_not_allowed` |
| 409 | [Idempotency conflict](/wispist/problems/idempotency-conflict.md) | `idempotency_conflict` |
| 409 | [Quota exceeded](/wispist/problems/quota-exceeded.md) | `quota_exceeded` |
| 412 | [Revision conflict](/wispist/problems/revision-conflict.md) | `revision_conflict` |
| 413 | [Request too large](/wispist/problems/request-too-large.md) | `request_too_large` |
| 415 | [Unsupported media type](/wispist/problems/unsupported-media-type.md) | `unsupported_media_type` |
| 428 | [Precondition required](/wispist/problems/precondition-required.md) | `precondition_required` |
| 429 | [Rate limited](/wispist/problems/rate-limited.md) | `rate_limited` |
| 503 | [Temporarily unavailable](/wispist/problems/temporarily-unavailable.md) | `temporarily_unavailable` |

HTTP headers retain their normal authority. `Retry-After`, `ETag`, `Allow`, and `WWW-Authenticate` are not replaced by Problem Details members.

---

# Invalid request

_Wispist / Problems_

> HTTP 400 problem returned when a Wispist path, query, identifier, cursor, precondition combination, or JSON envelope is invalid.

Canonical type:

```text
https://learn.peios.org/wispist/problems/invalid-request/
```

Status: `400 Bad Request`. Browser code: `invalid_request`.

Wispist returns this problem when the request cannot be interpreted under the v1 resource contract. Examples include an unknown or repeated query parameter, invalid collection or document ID, non-canonical path, malformed cursor, unknown request-envelope member, or contradictory conditional headers.

This differs from [Invalid JSON](/wispist/problems/invalid-json.md): the JSON may be syntactically valid while its protocol envelope or request metadata is not.

Correct the request rather than retrying it unchanged. Use the browser client where possible; it constructs canonical paths, envelopes, pagination, and mutation preconditions.

---

# Invalid JSON

_Wispist / Problems_

> HTTP 400 problem returned for malformed, ambiguous, non-object, deeply nested, invalid UTF-8, or otherwise disallowed document JSON.

Canonical type:

```text
https://learn.peios.org/wispist/problems/invalid-json/
```

Status: `400 Bad Request`. Browser code: `invalid_json`.

The submitted document representation is not permitted JSON. Wispist rejects malformed or invalid UTF-8 input, a `data` root that is not an object, duplicate object member names, more than 32 nesting levels, keys longer than 256 UTF-8 bytes, and trailing JSON values.

Generate the body with a normal JSON serialiser and pass a non-null object to the browser client. Duplicate members must be fixed at the producer; Wispist does not choose which ambiguous value wins.

An oversized representation uses [Request too large](/wispist/problems/request-too-large.md) instead.

---

# Authentication required

_Wispist / Problems_

> HTTP 401 problem returned when collection policy requires an authenticated principal and the Wispist host supplied an anonymous caller.

Canonical type:

```text
https://learn.peios.org/wispist/problems/authentication-required/
```

Status: `401 Unauthorized`. Browser code: `authentication_required`.

The collection operation is declared `authenticated`, but the host did not bind an authenticated principal. The response includes `WWW-Authenticate`.

Wispdeck's initial hosted-site integration supplies anonymous principals to public and preview JavaScript. A dashboard session or private preview cookie does not automatically become Wispist identity. Until a host identity integration is configured, change the operation to `anyone` only if public access is genuinely intended, or leave it inaccessible.

An authenticated principal that still lacks permission receives [Forbidden](/wispist/problems/forbidden.md).

---

# Forbidden

_Wispist / Problems_

> HTTP 403 problem returned when Wispist policy, origin enforcement, or a host read-only override denies an operation.

Canonical type:

```text
https://learn.peios.org/wispist/problems/forbidden/
```

Status: `403 Forbidden`. Browser code: `forbidden`.

The bound principal may not perform the operation. Common causes are:

- The collection operation is `nobody`.
- A mutation did not carry the exact bound site `Origin`, or Fetch Metadata identified it as cross-site.
- The request is using Wispdeck's Current preview, whose live-data binding is unconditionally read-only.
- A configured authoriser denied the current or proposed document.

Do not retry unchanged. Check `wispist.readOnly` to adjust Current-preview UI, and compare the active release's `wispist.json` with the operation being attempted. Client metadata is only a UI hint; the server remains authoritative.

---

# Not found

_Wispist / Problems_

> HTTP 404 problem returned when a Wispist resource, declared collection, document, or retained store state is unavailable.

Canonical type:

```text
https://learn.peios.org/wispist/problems/not-found/
```

Status: `404 Not Found`. Browser code: `not_found`.

The requested resource is unavailable. A document may not exist, the active release may not declare the collection, or the path may not identify a v1 resource.

Wispist does not reveal hidden stored documents merely because an older release once declared them. Removing a declaration makes the collection unavailable without deleting its data.

For `get`, treat this as an absent document where that is valid application state. For an entire collection, inspect the declaration attached to the release serving this request.

---

# Method not allowed

_Wispist / Problems_

> HTTP 405 problem returned when the Wispist path identifies a known resource but that resource does not implement the request method.

Canonical type:

```text
https://learn.peios.org/wispist/problems/method-not-allowed/
```

Status: `405 Method Not Allowed`. Browser code: `method_not_allowed`.

The path names a known Wispist resource, but its method is not supported. The `Allow` response header lists valid methods and is authoritative.

For example, a collection document resource supports `GET`, `HEAD`, `PUT`, and `DELETE`, while the change stream supports only `GET`. Use the route table in the [HTTP API reference](/wispist/reference/http-api.md) or the browser client.

---

# Idempotency conflict

_Wispist / Problems_

> HTTP 409 problem returned when a generated-ID POST reuses a live idempotency key for a different request fingerprint.

Canonical type:

```text
https://learn.peios.org/wispist/problems/idempotency-conflict/
```

Status: `409 Conflict`. Browser code: `idempotency_conflict`.

The `Idempotency-Key` was already used in this namespace for a different generated-ID POST. Wispist retains each successful key and response for at least 24 hours so a lost response can be retried safely.

If this is a retry, send exactly the same method, path, and document data. If it is a new logical create, generate a new strong random key. The built-in `collection.add` method does this automatically.

This problem concerns POST request identity. Concurrent document revisions use [Revision conflict](/wispist/problems/revision-conflict.md).

---

# Quota exceeded

_Wispist / Problems_

> HTTP 409 problem returned when a mutation would exceed a Wispist collection document count, namespace byte limit, or idempotency-record bound.

Canonical type:

```text
https://learn.peios.org/wispist/problems/quota-exceeded/
```

Status: `409 Conflict`. Browser code: `quota_exceeded`.

The mutation is valid in isolation but would exceed a storage bound. The transactional check may cover collection document count, total live or draft document bytes, or the maximum number of unexpired idempotency records.

Delete data that is no longer needed, reduce the proposed document, or ask the operator to review installation policy. A release declaration may lower limits but cannot raise them.

A single request or document that exceeds its byte limit uses [Request too large](/wispist/problems/request-too-large.md).

---

# Revision conflict

_Wispist / Problems_

> HTTP 412 problem returned when a create-only ID exists or a replacement or deletion no longer matches the observed document revision.

Canonical type:

```text
https://learn.peios.org/wispist/problems/revision-conflict/
```

Status: `412 Precondition Failed`. Browser code: `revision_conflict`.

The mutation's HTTP precondition did not match current state:

- `If-None-Match: *` was used to create an ID that already exists.
- `If-Match` carried an older revision for replacement or deletion.

Fetch or wait for the latest document, reconcile the user's intent, then submit a new conditional mutation using that revision. Never loop blindly: another caller may have made a meaningful conflicting change.

The browser client's `replace`, `update`, and `delete` methods accept an observed document envelope so safe concurrency is the normal path.

---

# Request too large

_Wispist / Problems_

> HTTP 413 problem returned when a Wispist request body or document representation exceeds its configured byte allowance.

Canonical type:

```text
https://learn.peios.org/wispist/problems/request-too-large/
```

Status: `413 Content Too Large`. Browser code: `request_too_large`.

The request body or `data` representation exceeds the active collection limit. Wispist limits the body before decoding and allows only a small fixed amount beyond the document limit for the JSON envelope.

Reduce the document or split independent records into separate documents. A release may request a higher collection limit only up to the operator's host maximum.

Total namespace storage exhaustion uses [Quota exceeded](/wispist/problems/quota-exceeded.md).

---

# Unsupported media type

_Wispist / Problems_

> HTTP 415 problem returned when a Wispist document mutation does not declare an application/json request body.

Canonical type:

```text
https://learn.peios.org/wispist/problems/unsupported-media-type/
```

Status: `415 Unsupported Media Type`. Browser code: `unsupported_media_type`.

Document create and replacement bodies require:

```http
Content-Type: application/json
```

Media-type parameters such as `charset=utf-8` are accepted, but another base type or a missing header is not. Set the header and send the documented `{"data": {...}}` envelope.

JSON that has the correct media type but invalid content uses [Invalid JSON](/wispist/problems/invalid-json.md) or [Invalid request](/wispist/problems/invalid-request.md).

---

# Precondition required

_Wispist / Problems_

> HTTP 428 problem returned when a Wispist replacement or deletion omits the conditional revision required for safe concurrency.

Canonical type:

```text
https://learn.peios.org/wispist/problems/precondition-required/
```

Status: `428 Precondition Required`. Browser code: `precondition_required`.

Wispist does not offer unconditional last-write-wins mutation. A selected-ID create requires `If-None-Match: *`. Replacing or deleting an existing document requires `If-Match` with the quoted observed revision.

Read the current document and use `replace`, `update`, or `delete` with that complete envelope. If the supplied revision is no longer current, the next response is [Revision conflict](/wispist/problems/revision-conflict.md).

---

# Rate limited

_Wispist / Problems_

> HTTP 429 problem returned when a bounded Wispist request token bucket or concurrent change-stream allowance has been exhausted.

Canonical type:

```text
https://learn.peios.org/wispist/problems/rate-limited/
```

Status: `429 Too Many Requests`. Browser code: `rate_limited`.

A read, mutation, generated-ID create, installation-wide request, or concurrent change stream exceeded its bounded allowance. `Retry-After` gives the minimum server-selected delay in seconds.

Wait for that delay and retry with backoff. Avoid tight loops, duplicate subscriptions, and unnecessary full-list refreshes. The built-in subscription client already reconnects with bounded exponential backoff.

Rate limiting is an availability control. It does not change collection policy or make public writes private.

---

# Temporarily unavailable

_Wispist / Problems_

> HTTP 503 problem returned when the Wispist store is busy, its bounded open-store cache is saturated, or an internal operation cannot safely complete.

Canonical type:

```text
https://learn.peios.org/wispist/problems/temporarily-unavailable/
```

Status: `503 Service Unavailable`. Browser code: `temporarily_unavailable`.

Wispist could not safely complete the operation. Expected transient causes include a busy SQLite store or temporary saturation of the bounded open-store cache. The response may include `Retry-After`.

Retry idempotent reads after a short backoff. Retry generated-ID POST only with the same idempotency key and identical request. Conditional replacements and deletions may be retried with the same observed revision; a committed competing change then becomes [Revision conflict](/wispist/problems/revision-conflict.md).

If the problem persists, give the operator the `X-Request-ID`. Wispist does not expose internal error text in the response.
