Skip to content

Deployment Host Preparation Owed

URI: https://docs.ankatech.co/errors/byok-host-preparation-owed
HTTP Status: 422 Unprocessable Entity

When you see this

A per-tenant BYOK declare named a key-protection mechanism whose class requires vendor preparation on the deployment host, and this deployment has not completed it. The request was refused before anything was written and before anything was contacted — no declaration was recorded, no coordinates were stored, no credential was unsealed, and the PIN carried in this very request never reached a C_Login against the deployment's token.

The body is not wrong. It is syntactically valid, the backend token is one of the nine, and the tenant is entitled to declare it. What refuses is the state of a plane the tenant does not administer.

The gate is a conjunction, and never one conjunct alone:

  1. the declared mechanism's class is host-bound, and
  2. this deployment has not declared and provisioned it.

Either half on its own is wrong in a way that matters. The class question alone is true for all five Cloud KMS tokens, which have no host at all, and it also refuses every appliance declare on a correctly prepared appliance deployment.

Common Causes

  • An appliance mechanism (cloudhsm, luna, nshield) declared on a deployment whose own host-side backend has not been prepared and bound. The deployment administrator enrols the partition or Security World, stages the vendor's PKCS#11 library, and binds the deployment's own backend first.
  • A tenant administrator working from a form that was not refreshed after the deployment's backend state changed. The console projects the same verdict per token and withholds the submit while it is true, so a form that offers the control is a stale form.

Response Example

{
  "type": "https://docs.ankatech.co/errors/byok-host-preparation-owed",
  "title": "Deployment Host Preparation Owed",
  "status": 422,
  "detail": "This key-protection mechanism requires preparation on the deployment host, and this deployment has not completed it. Only the deployment administrator can perform that work; ask them to complete it before declaring this backend. Nothing was contacted and nothing was changed.",
  "instance": "/api/v3/admin/tenants/7d1c9b40-52e8-4a36-9f07-c3b81e6d240a/key-backend/byok",
  "timestamp": 1730000000,
  "extensions": {
    "requestId": "d6194e72-3f0b-4a85-81c9-4b70e2ad5163"
  }
}

Content-Type: application/problem+json — the response follows RFC 9457 Problem Details.

How to Resolve

  1. A tenant administrator cannot clear this, and the refusal says so. It names the deployment administrator as the owner of the work. Ask them.
  2. The deployment administrator prepares the host and binds the deployment's own backend — the per-vendor runbooks are under Integrations → Key-Protection Backend → Setup (CloudHSM, Luna, nShield*).
  3. Re-submit the declare, or declare a mechanism whose class owes the host nothing — any of the five Cloud KMS backends.

The refusal discloses no deployment host state. No module path, no token label, no slot or serial, no vendor library name or presence, no reachability statement, and not the declared deployment backend. It does not echo the caller's own submitted token either: detail is one fixed constant, byte-identical for every backend on every deployment, which is what makes it useless as a probe. There are no extension members beyond requestId — a field extension would be false, because no field of the request is wrong.

It cannot become an existence oracle. The gate is evaluated last among the pre-404 refusals and before tenant existence. Its verdict is two columns of the deployment singleton and a manifest row, so it carries no tenant data and cannot leak whether the target tenant exists; evaluating it after the 404 would have made an appliance declare on a non-existent tenant answer 404 and turned the refusal into exactly that oracle. Both deployment reads happen on both verdicts, so the two answers cost the same.

The console should not produce this refusal. The same verdict is projected per token as hostPreparationOwed on each entry of the declarable-backends projection, and the form withholds the declare submit while it is true. That member is a second verdict beside admissible, because the admission fold's four factors cannot express host readiness — without it the projection would report admissible: true for a backend the very next declare answers 422.

Where it is raised

  • PUT /api/v3/admin/tenants/{tenantId}/key-backend/byok — the per-tenant BYOK declare
  • PUT /api/v3/admin/platform/tenants/{tenantId}/key-backend/byok — its platform-plane twin

The pre-commit rehearsals (POST .../key-backend/byok/validate and its platform twin) do not raise it: the gate runs inside the declare. Read the projected hostPreparationOwed verdict before submitting instead — that is the member it exists for.

Audit. The refusal is audited before it propagates, through the same FAILURE arm the other three declare refusals use, under the reason token HOST_PREPARATION_OWED — so one query answers "which declares were refused, and why" for every cause rather than for three of four.

* restricted content — see higher-tier documentation