---
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-22T13:45:17.679094+00:00
updated: 2026-09-22T13:45:17.679094+00:00
tags: ["errors", "parallax", "reference"]
url: https://xiobjects.com/docs/xio/parallax/rest-api/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-22T13:44:10.4660407\u002B00:00",
  "encoding": "utf-8",
  "render_intent": "markdown",
  "title": "Errors and Refusals",
  "slug": "xio/parallax/rest-api/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. |

One refusal carries a response header rather than an extension.

| Header | On | Description |
|--------|-----|-------------|
| `Retry-After` | `503 Image could not be checked yet` | Whole seconds to wait before sending the same request again. |

## 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` | The engine could not check this image yet, 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/rest-api/manifests). | Fix the named part. |
| `Too many declared hashes` | 400 | `The declared hash list exceeds the configured cap.` | Split the call. Both resume conversations and `POST /records` 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. `GET /records/{originalImageHash}` answers it for a bad path hash. |
| `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/rest-api/quotas-and-stats). |
| `Image could not be checked yet` | 503 | `This image could not be checked against registered originals yet. Nothing was registered, answered or charged; send it again.` | The engine is there but could not answer for this image yet. Wait the seconds `Retry-After` names, then send the same request again. |
| `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 could not be checked against registered originals yet. Nothing was registered, answered or charged; send it again.` | The engine could not answer for this image yet. Nothing is wrong with the image. | Upload it again in a little while. Its siblings in the same request are untouched. |
| `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/rest-api/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. |
| `retry` | The engine could not answer this entry yet. Nothing charged. |

`retry` is not a failure. Nothing is wrong with the image, and nothing about it is already registered. The engine simply could not answer for it at that moment, and the same image sent again can be answered.

A commit is terminal, so the service never re-sends the entry for you. Send that image again yourself, in a new slot or through the single-image route. Whatever the rest of the slot registered or answered is untouched, and the retried entry was not billed.

`GET /slots/{slotId}/progress` counts `retry` apart from `failed` and from `errata`, so a poll tells you which entries are worth sending again.

## Published-record outcomes

`GET /records/{originalImageHash}` and `POST /records` report one outcome per hash. Three are answers about the record; two report a refusal of that one hash.

| `outcome` | Meaning | Charged |
|-----------|---------|---------|
| `published` | A live record answered. `manifests` and `verification` carry it. | Yes |
| `takenDown` | The registrant took the image down. Nothing else is returned. | Yes |
| `noRecordAnswered` | No record answered for this hash. Either nothing was ever published for it, or the record store would not answer right now. You cannot tell which. | No |
| `refused` | The engine would not answer for this hash. `failureReason` says why. | No |
| `retry` | The engine could not answer for this hash yet. Ask again shortly. | No |

`noRecordAnswered` is a `200`, not a `404`. The service cannot distinguish "never registered" from "unreachable", so it does not pretend to.

On the single route, `refused` and `retry` are statuses instead of outcomes: `422 Image could not be checked` and `503 Image could not be checked yet`. The batch route reports all five inside its `200`, so one bad hash never costs its siblings their answers.

## 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.

## Engine refusal names you may see today

These are the names the engine build behind this deployment can send. The table is a reading aid, not a contract: the list can grow with an engine release, and the API does not check a name against it.

| Name | Meaning | What to do |
|------|---------|------------|
| `XioRegistrationPacketWithheldException` | The engine could not answer for this image yet. A pool it expects to be live was not among the pools your item reached, which is what a fleet restore looks like from the outside. | Send the image again. On a single route this is the `503` above, with `Retry-After`; in a batch it is the `retry` state. |
| `XioLookUpNotAnsweredException` | A record was found, but its proofs did not verify, so the engine would not answer on it. | Nothing to fix on your side. Contact the operator if it repeats. |
| `XioRegisterManifestsRejectedException` | The image's manifest list is past the engine's own bounds, in entries or in total payload bytes. | Send fewer or smaller manifests. See [Manifests](/docs/xio/parallax/rest-api/manifests). |
| `XioManifestKindNotRegisteredException` | The engine does not know one of the manifest kinds you stated. | Use a kind the engine knows, or drop that manifest. |
| `XioRegistrationPacketRejectedException` | The material held for this image was altered, is of an older format, or was made under a different engine build. | Upload the image again into a fresh slot. |
| `XioRegisterInFlightBudgetExceededException` | The register of this one image ran past the engine's own time budget. | Send it again. |
| `XioRegistrationIngestRefusedException` | The engine could not take this image into a pool: the pool instance was unreachable, faulted, refused the record, or the request was too large for its channel. Nothing was written, signed or published for it. | Send it again. |
| `XioQueryMaterialRejectedException` | The query material derived for this image is of a format the engine does not accept. | Send the image again. Contact the operator if it repeats. |
| `XioPoolJournalSequenceException` | The engine's own pool journal was out of sequence for this item. | Send it again. |
| `XioPoolJournalOwnershipException` | Two engine instances claimed the same journal position for this item. | Send it again. |
| `PoolInstanceUnreachableException` | The pool instance holding this work could not be reached. | Send it again. |
| `PoolInstancePayloadTooLargeException` | The request for this one item was too large for the pool instance's channel. | Send a smaller image. |

Only the first of these is worth asking again for immediately, and the API says so itself: it is the only one that answers `503 Image could not be checked yet` on a single route, or `retry` on a batch entry. Every other name arrives as `422 Image could not be checked` or as a `failed` entry.

## 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.

A published record is the one deliberate exception, and it is not an isolation failure. Any token holder may read any image's record, whoever registered it, and the answer names no account and no registration. See [Recover an image's attribution](/docs/xio/parallax/rest-api/records).
<!-- xion:trust
{
  "v": 1,
  "canon_v": 1,
  "ctx": "xiobjects.com/content",
  "hash_blake3_hex": "26569f52749d23b49c16b937867a9b0ff4384bfafe900272eb905e9758c135b4",
  "hash_sha256_hex": null,
  "sig_alg": "ed25519",
  "sig_b64": "L2irjDyR6_WawT1P20gE7hCCXGtO2Eu2hCIWHi43ujqO30uGBrdTIwgkc57kE7gFOwRg5fYDfyFU6kZCiMVGBw",
  "pubkey_b64": "o4j1lUqY2QaQO9ku_UzWfnOV7E_MtWC6piiZeHNczzc",
  "x509_chain_pem": [
    "-----BEGIN CERTIFICATE-----\r\nMIIB9TCCAaegAwIBAgIRAMLfVK/j36eoLugMlAxtV9UwBQYDK2VwMC4xLDAqBgNV\r\nBAMMI1hJIE9iamVjdHMgSW5jIENvbnRyb2wgSW50ZXJtZWRpYXRlMB4XDTI2MDky\r\nMjEzMDUyM1oXDTI2MTAyMjEzMDUyM1owSzEeMBwGA1UEAwwVeGlvLWNvbnRlbnQt\r\ncHVibGlzaGVyMRcwFQYDVQQKDA5YSSBPYmplY3RzIEluYzEQMA4GA1UECwwHQ29u\r\ndGVudDAqMAUGAytlcAMhAKOI9ZVKmNkGkDvZLv1M1n5zlexPzLVguqYomXhzXM83\r\no4G8MIG5MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG\r\nCCsGAQUFBwMkMGUGA1UdIwReMFyAFDspt5hZsP6rNX4Cq7owpMYa05OyoS6kLDAq\r\nMSgwJgYDVQQDDB9JbnN0aXR1dGUgb2YgUHJvdmVuYW5jZSBSb290IENBghRSYDf4\r\nsUJ\u002B9h\u002Bod0\u002BZRK/X/JSUBTAdBgNVHQ4EFgQUy/uoDe3CJ5COIHD70XDiFWqA5zMw\r\nBQYDK2VwA0EAWpQV7vabZn8eh/eHf6TIdGqd/RmpyO1xcS8LYJ2OxbXI/IF9GojA\r\nMsuqTH4E6DFE\u002BZLRQAUjKw6pG67qRE40CQ==\r\n-----END CERTIFICATE-----\r\n",
    "-----BEGIN CERTIFICATE-----\r\nMIIByDCCAXqgAwIBAgIUUmA3\u002BLFCfvYfqHdPmUSv1/yUlAUwBQYDK2VwMCoxKDAm\r\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\r\nMDMxNzEyWhcNMzAxMTAxMDMxNzEyWjAuMSwwKgYDVQQDDCNYSSBPYmplY3RzIElu\r\nYyBDb250cm9sIEludGVybWVkaWF0ZTAqMAUGAytlcAMhAFSS/pggSRmTcAMko7uc\r\nATH8OHgxVymd5mBFlPXbJkgio4GtMIGqMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD\r\nVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQ7KbeYWbD\u002BqzV\u002BAqu6MKTGGtOTsjBlBgNV\r\nHSMEXjBcgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQKEupCwwKjEoMCYGA1UEAwwfSW5z\r\ndGl0dXRlIG9mIFByb3ZlbmFuY2UgUm9vdCBDQYIUJqoJlpiSFg\u002B7W5IJLMrLttgR\r\nQp4wBQYDK2VwA0EA5FOht7YOsVRPp/FOKMQ\u002B3Mo9JxrvGR3ylKWAWNm6OUV7N3DB\r\nI9cD62wU5I0d0EKDBy0CX9DnoqUyxv5yguraAA==\r\n-----END CERTIFICATE-----\r\n",
    "-----BEGIN CERTIFICATE-----\r\nMIIBaTCCARugAwIBAgIUJqoJlpiSFg\u002B7W5IJLMrLttgRQp4wBQYDK2VwMCoxKDAm\r\nBgNVBAMMH0luc3RpdHV0ZSBvZiBQcm92ZW5hbmNlIFJvb3QgQ0EwHhcNMjUxMTAy\r\nMDMwNTEyWhcNMzUxMDMxMDMwNTEyWjAqMSgwJgYDVQQDDB9JbnN0aXR1dGUgb2Yg\r\nUHJvdmVuYW5jZSBSb290IENBMCowBQYDK2VwAyEAEWNZl\u002Br3IC7\u002BgBh90Yo1kWk1\r\npZCVzVuFdFT7qBBU8W2jUzBRMB0GA1UdDgQWBBQAZRTDswSVORu\u002BkUOKX6WvrOvm\r\nQDAfBgNVHSMEGDAWgBQAZRTDswSVORu\u002BkUOKX6WvrOvmQDAPBgNVHRMBAf8EBTAD\r\nAQH/MAUGAytlcANBAO6QeydOFNrN75qNyftggYudsxMyl4w9qWkSdZ6hlhrRcbSr\r\niG9Si0kbrIJOwYB/LTBU0RM4Rl\u002Bo9PM3Qp0mPwo=\r\n-----END CERTIFICATE-----\r\n"
  ],
  "key_id": "Y3qJjjUQD3bpQwrMcz5raLrfTVObfcxBKRbUELm3-wc",
  "created_at": "2026-09-22T13:44:10Z"
}
-->