---
title: "Errors and Refusals"
description: "Every status XI Parallax answers, every refusal reason and per-entry outcome an integrator can receive, and what to do about each."
published: 2026-09-21T23:44:12.231994+00:00
updated: 2026-09-21T23:44:12.231994+00:00
tags: ["errors", "parallax", "reference"]
url: https://xiobjects.com/docs/xio/parallax/errors-and-refusals
source: XI Objects
---

<!-- xion:doctype xion+markdown -->
<!-- xion:metadata
{
  "version": "1.0",
  "content_type": "application/xion\u002Bmarkdown",
  "source_type": "xi-content/doc",
  "generator": "xio-content-publisher/1.0.0",
  "generated": "2026-09-21T23:42:01.5947668\u002B00:00",
  "encoding": "utf-8",
  "render_intent": "markdown",
  "title": "Errors and Refusals",
  "slug": "xio/parallax/errors-and-refusals",
  "copyright": "\u00A9 2026 XI Objects Inc"
}
-->

# Errors and Refusals

## The problem details envelope

Every refusal answers `application/problem+json`.

| Field | Type | Description |
|-------|------|-------------|
| `title` | `string` | A short name for the condition. Match on this, not on `detail`. |
| `status` | `int` | The HTTP status, repeated in the body. |
| `detail` | `string` | A fixed sentence. It never quotes your image, your manifest or another account back at you. |
| `traceId` | `string` | The request's trace identifier. |
| `type` | `string` | A stable URN naming the condition, such as `urn:xio:parallax:problem:slot-not-found`. Always present on a refusal. |

One condition has one answer everywhere. The same thing going wrong gives you the same status, the same `title`, the same `type` and the same `detail`, whichever route you were calling. Branch on `title` or on `type`, and read `detail` for which case it was.

Extensions appear on specific refusals only.

| Extension | On | Description |
|-----------|-----|-------------|
| `cap` | `409 Open-slot cap reached` | The number of open slots your account may hold. |
| `registrationId` | `409 Image not registered` | The registration that already covers the image, when it is yours. Absent when the match is another account's. |
| `registrationRemaining` | `429 Quota exceeded` | Registrations left. |
| `lookupRemaining` | `429 Quota exceeded` | Look-ups left. |

## Every status

| Status | When |
|--------|------|
| `200 OK` | The call succeeded. A per-image or per-entry body can still carry individual refusals. |
| `201 Created` | A slot or look-up slot was opened. `Location` points at it. |
| `204 No Content` | An entry was removed, a slot was abandoned, or a registration was taken down. |
| `400 Bad Request` | The request shape is wrong: not multipart, wrong parts, a query-string parameter where none is accepted, a bad manifest part, a bad declared hash list. |
| `401 Unauthorized` | No usable bearer token. Missing, malformed, unknown and revoked are all the same answer. |
| `404 Not Found` | The route does not exist, or the slot, entry or registration id names nothing you own. Another account's id reads identically to one that never existed. |
| `405 Method Not Allowed` | The route exists but not for that method. |
| `409 Conflict` | The open-slot cap is reached, the slot is no longer open, or the image is already registered. |
| `413 Content Too Large` | The request, the image part or a manifest part is past its size cap. |
| `415 Unsupported Media Type` | The image part's content type is not accepted. |
| `422 Unprocessable Content` | The engine would not check this image. Nothing was registered, answered or charged. |
| `429 Too Many Requests` | Your registration or look-up grant does not cover the request. This is a lifetime grant, not a rate limit. |
| `500 Internal Server Error` | An unexpected server error. Retry, then contact the operator. |
| `503 Service Unavailable` | No engine is configured, or a take-down was refused on its engine record. |

## Whole-request refusals by title

Every title below is the whole vocabulary. No route invents one of its own.

| Title | Status | `detail` | What to do |
|-------|--------|----------|------------|
| `Malformed request` | 400 | `The request Content-Type must be multipart/form-data with a boundary.` | Send a multipart body with a boundary. |
| `Malformed request` | 400 | `The request body must be a JSON object.` | Send a JSON object. |
| `Malformed request` | 400 | `POST /registrations accepts manifest parts only before the image part.` | Move every manifest part before the image part. |
| `Malformed request` | 400 | `POST /registrations accepts exactly one image file part.` | Send one image, not several. |
| `Malformed request` | 400 | `POST /registrations accepts only manifest parts and exactly one image part.` | Remove the extra field. |
| `Malformed request` | 400 | `POST /registrations requires exactly one image file part.` | Add the image part. |
| `Malformed request` | 400 | `POST /lookup accepts no query-string parameters.` | Drop the query string. There is no top-K or threshold. |
| `Malformed request` | 400 | `POST /lookup requires exactly one well-formed multipart image file part.` | Send one multipart file part. |
| `Malformed request` | 400 | `POST /lookup requires a well-formed multipart body.` | The body ended where a part should begin. Re-send. |
| `Malformed request` | 400 | `POST /lookup accepts exactly one image file part and no other part.` | Remove the extra part. |
| `Malformed request` | 400 | `POST /lookup requires exactly one image file part.` | Add the image part. |
| `Malformed request` | 400 | `Uploading lookup queries accepts no query-string parameters.` | Drop the query string. |
| `Malformed request` | 400 | `Uploading lookup queries requires a multipart body of image file parts.` | Send a multipart body with a boundary. |
| `Malformed request` | 400 | `Uploading lookup queries requires a well-formed multipart body.` | The body ended mid part. Re-send. |
| `Malformed request` | 400 | `Uploading lookup queries accepts one or more image file parts and no other field.` | A look-up carries the image alone. Nothing was held. |
| `Malformed request` | 400 | `Uploading lookup queries accepts at most N image file parts.` | Split the batch. Nothing was held. |
| `Invalid manifest` | 400 | A manifest part refusal sentence. See [Manifests](/docs/xio/parallax/manifests). | Fix the named part. |
| `Too many declared hashes` | 400 | `The declared hash list exceeds the configured cap.` | Split the resume call. Both conversations answer this. |
| `Invalid declared hash` | 400 | `'<value>' is not a valid, unique 64-character lowercase hex image hash.` | Send 64 lowercase hex characters, each hash once. |
| `Slot not found` | 404 | `No slot '<id>' exists.` | The id is unknown, expired or not yours. A look-up slot answers this too. |
| `Entry not found` | 404 | `No entry '<hash>' exists in slot '<id>'.` | The slot holds no entry under that hash, on either conversation. |
| `Registration not found` | 404 | `No registration exists.` | The id is unknown, already taken down or not yours. |
| `Open-slot cap reached` | 409 | `This account already holds N open slots.` | Commit or abandon a slot. `cap` names the limit. |
| `Slot is no longer open` | 409 | `Slot '<id>' is committed or abandoned and no longer accepts this request.` | The slot is terminal. Open a new one. |
| `Image not registered` | 409 | `This image is already registered.` or `This image is a derivative of an image that is already registered.` | Take the named registration down first, or accept that the image is spoken for. |
| `Request too large` | 413 | `The request exceeds the configured per-request upload size cap.` | Send less in one request. |
| `Image too large` | 413 | `The image exceeds the configured per-image size cap.` | Send a smaller image. |
| `Manifest too large` | 413 | `The manifest part exceeds the configured size cap.` | Send a smaller manifest. |
| `Unsupported image content type` | 415 | `The image part's content type is not accepted.` | Send an accepted image media type. |
| `Image could not be checked` | 422 | `This image could not be checked against registered originals.`, the engine's own refusal sentence, or `This query image could not be stored for look-up.` | The engine declined, or the image could not be stored. Nothing registered, answered or charged. Retry. |
| `Quota exceeded` | 429 | `The account's remaining quota does not cover this request.` | Out of grant. See [Quotas and stats](/docs/xio/parallax/quotas-and-stats). |
| `Engine not configured` | 503 | `No extraction engine is configured to service this request.` | The deployment has no engine. Nothing registered, nothing charged. |
| `Take-down refused` | 503 | `The registration could not be taken down on its engine record.` | The registration is untouched. Contact the operator. |

## Per-image rejection reasons

These arrive in an upload outcome's `rejectionReason`, with `accepted: false`. They refuse one image and leave its siblings in the same request alone.

| `rejectionReason` | Meaning | What to do |
|-------------------|---------|------------|
| `Unsupported image content type.` | The part's content type is not accepted. | Send an accepted image media type. |
| `Image exceeds the per-image size cap.` | This image is past the per-image cap. | Send a smaller image. |
| `Request exceeds the per-request size cap.` | This image would push the request past its cap. | Split the batch. |
| `The account's remaining registration grant does not cover this image.` | No registration grant left for a new image. Carries both remaining counts. | Commit or free a slot, or ask for more grant. |
| `The account's remaining lookup grant does not cover this image.` | No look-up grant left. On a registration upload this refuses the pre-check before any engine call. Carries both remaining counts. | Free a look-up slot, or ask for more grant. |
| `This image could not be checked against registered originals.` | The engine declined the pre-check. The image is never accepted unchecked. | Retry. |
| `This image is already registered.` | These exact bytes already have a live registration. `registrationId` names it when it is yours. | Take that registration down, or leave the image alone. |
| `This image is a derivative of an image that is already registered.` | A different original that this image derives from is registered. | Nothing to do here. |
| `This slot already remembers the maximum number of images it will not register.` | The slot's errata ceiling is reached. Costs nothing. | Remove errata entries, or use a new slot. |
| `This lookup slot already holds the maximum number of query images.` | The look-up slot's ceiling is reached. | Commit this slot and open another. |
| `This query image could not be stored for look-up.` | The held-bytes store refused this image. Siblings are untouched. | Retry that image. |
| `Slot is no longer open.` | The slot went away while the request was running. | Open a new slot and re-upload. |
| Any manifest part sentence | See [Manifests](/docs/xio/parallax/manifests). | Fix that image's manifest parts. |

## Per-entry commit outcomes

`POST /slots/{slotId}/commit` and `GET /slots/{slotId}/progress` report one state per entry.

| `state` | Meaning |
|---------|---------|
| `held` | Not yet committed, or the commit never reached it. Nothing charged. |
| `registered` | Registered. `registrationId` names it. |
| `errata` | Never registered, because an original of the image is already registered. Nothing charged. |
| `failed` | Could not be committed. `failureReason` says why. Nothing charged. |
| `answered` | A look-up query reached a verdict. Registration slots never use it. |

## Errata reasons

An errata entry's `notRegisteredReason` on the manifest report, or `failureReason` on progress and commit, carries one of two sentences.

| Sentence | Meaning |
|----------|---------|
| `This image is already registered.` | These exact bytes already have a live registration. |
| `This image is a derivative of an image that is already registered.` | A different original that this image derives from is registered. |

An errata can arrive three ways, all with the same sentences and all uncharged: the pre-check refused the image at upload; the service found at commit that your account already holds a registration for that exact image hash; or the engine answered already-registered at commit for an image whose content now matches a registration that landed after your upload.

## Per-entry failure reasons

| `failureReason` | Meaning | What to do |
|-----------------|---------|------------|
| `The engine refused this item: <Name>. <message>` | The engine refused this one entry. `<Name>` is the engine's own name for the refusal. The message is bounded and stripped of control characters, and is omitted when it is empty. A refused item is never read as no match and never accepted. | Act on the name. A manifest kind the engine does not know shows up here. |
| `The engine refused this item: StoredRegistrationPacketUnreadable. The stored registration packet could not be read.` | The packet held on this entry cannot be read back, usually because the engine was upgraded while your slot was open. | Upload the image again into a fresh slot. |
| `No stored extraction was found for this entry.` | The entry carries no registration packet. | Remove the entry and upload the image again. |
| `No pooled query image was found for this entry.` | The query image's pixels were gone before the commit read them. | Upload the query again. |
| `No extraction engine is configured to service this request.` | The engine refused the whole call. | Retry when the service reports healthy. |
| `This query's verdict could not be charged against the account's lookup grant.` | The verdict could not be billed, so it is not returned. Nothing charged. | Check your look-up grant. |
| `This entry could not be committed.` | An unexpected failure on this entry. Siblings are unaffected. | Retry the entry in a new slot. |

The set of engine refusal names is not fixed and is not enumerated anywhere in this API. A later engine release can add a name, and it reaches you with its own words rather than through a translation table that could fall behind. Log the whole sentence. Do not branch on names you have not seen.

## Not-found and isolation

Every ownership failure is a `404`, never a `403`. A slot, entry or registration belonging to another account answers byte for byte what an id that never existed answers, down to the field set. The only thing that differs is the id echoed in `detail`, which is the id you supplied.

A look-up slot and a registration slot answer the same `Slot not found`, and both conversations answer the same `Entry not found` for a hash that is not a valid image hash. Which conversation you were on is not something a refusal tells you.

This is deliberate. There is no way to probe this API for whether an id is real.
<!-- xion:trust
{
  "v": 1,
  "canon_v": 1,
  "ctx": "xiobjects.com/content",
  "hash_blake3_hex": "0465c06dc747a8a9c826c1a7d784f32b62e238e5a3e047130c79d2983fe19c42",
  "hash_sha256_hex": null,
  "sig_alg": "ed25519",
  "sig_b64": "H4aBS2k3ZZRYFH8fQmcbst0SaJHPuxVxhxCzIDxXyeZM-tnemhaBkgcs5yeIVvav0e83DT2lU0gJ31edO9nHAw",
  "pubkey_b64": "VrV0rqYGnIqutEwPTr11jfrjH9wmtkOlL68wL0W6ed0",
  "x509_chain_pem": [
    "-----BEGIN CERTIFICATE-----\nMIIB9DCCAaagAwIBAgIQbmGqXUijS3XuRUHDuVME8jAFBgMrZXAwLjEsMCoGA1UE\nAwwjWEkgT2JqZWN0cyBJbmMgQ29udHJvbCBJbnRlcm1lZGlhdGUwHhcNMjYwOTIx\nMjMzNjU1WhcNMjYxMDIxMjMzNjU1WjBLMR4wHAYDVQQDDBV4aW8tY29udGVudC1w\ndWJsaXNoZXIxFzAVBgNVBAoMDlhJIE9iamVjdHMgSW5jMRAwDgYDVQQLDAdDb250\nZW50MCowBQYDK2VwAyEAVrV0rqYGnIqutEwPTr11jfrjH9wmtkOlL68wL0W6ed2j\ngbwwgbkwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI\nKwYBBQUHAyQwZQYDVR0jBF4wXIAUOym3mFmw/qs1fgKrujCkxhrTk7KhLqQsMCox\nKDAmBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0GCFFJgN/ix\nQn72H6h3T5lEr9f8lJQFMB0GA1UdDgQWBBTnptRqwN8T\u002B5J0zUSRl65iscaPUzAF\nBgMrZXADQQBTzG1wSuUk70ymEN3Mj6XxsS1c5egjDoy\u002BW/V2kko5c2a1Cs7c/kiD\n6H2y9z1DNSH5qjzLZcm9JKPN0mjCy8MO\n-----END CERTIFICATE-----\n",
    "-----BEGIN CERTIFICATE-----\nMIIByDCCAXqgAwIBAgIUUmA3\u002BLFCfvYfqHdPmUSv1/yUlAUwBQYDK2VwMCoxKDAm\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\nMDMxNzEyWhcNMzAxMTAxMDMxNzEyWjAuMSwwKgYDVQQDDCNYSSBPYmplY3RzIElu\nYyBDb250cm9sIEludGVybWVkaWF0ZTAqMAUGAytlcAMhAFSS/pggSRmTcAMko7uc\nATH8OHgxVymd5mBFlPXbJkgio4GtMIGqMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD\nVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQ7KbeYWbD\u002BqzV\u002BAqu6MKTGGtOTsjBlBgNV\nHSMEXjBcgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQKEupCwwKjEoMCYGA1UEAwwfSW5z\ndGl0dXRlIG9mIFByb3ZlbmFuY2UgUm9vdCBDQYIUJqoJlpiSFg\u002B7W5IJLMrLttgR\nQp4wBQYDK2VwA0EA5FOht7YOsVRPp/FOKMQ\u002B3Mo9JxrvGR3ylKWAWNm6OUV7N3DB\nI9cD62wU5I0d0EKDBy0CX9DnoqUyxv5yguraAA==\n-----END CERTIFICATE-----\n",
    "-----BEGIN CERTIFICATE-----\nMIIBaTCCARugAwIBAgIUJqoJlpiSFg\u002B7W5IJLMrLttgRQp4wBQYDK2VwMCoxKDAm\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\nMDMwNTEyWhcNMzUxMDMxMDMwNTEyWjAqMSgwJgYDVQQDDB9JbnN0aXR1dGUgb2Yg\nUHJvdmVuYW5jZSBSb290IENBMCowBQYDK2VwAyEAEWNZl\u002Br3IC7\u002BgBh90Yo1kWk1\npZCVzVuFdFT7qBBU8W2jUzBRMB0GA1UdDgQWBBQAZRTDswSVORu\u002BkUOKX6WvrOvm\nQDAfBgNVHSMEGDAWgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQDAPBgNVHRMBAf8EBTAD\nAQH/MAUGAytlcANBAO6QeydOFNrN75qNyftggYudsxMyl4w9qWkSdZ6hlhrRcbSr\niG9Si0kbrIJOwYB/LTBU0RM4Rl\u002Bo9PM3Qp0mPwo=\n-----END CERTIFICATE-----\n"
  ],
  "key_id": "_A4EpsXvfBfVEGu5deK65yN4EjBJsUpgkdoS25zPEFk",
  "created_at": "2026-09-21T23:42:01Z"
}
-->