Skip to content

Key Operation Incompatible

URI: https://docs.ankatech.co/errors/key-operation-incompatible
HTTP Status: 400 Bad Request

When you see this

A re-encryption or a re-sign named two keys of different classes: one of them is symmetric and the other is asymmetric. Re-encrypt and re-sign move a payload from one key to another key of the same class, so a symmetric ↔ asymmetric pair is refused in both directions.

A composite (hybrid) key is asymmetric. A symmetric key paired with a SIMPLE asymmetric key or with a composite key is therefore refused the same way.

Core API returns this problem on four operations:

Operation detail
POST /api/v3/crypto/reencrypt Unsupported re-encryption: symmetric <--> asymmetric keys are not permitted
POST /api/v3/crypto/stream/reencrypt Unsupported re-encryption: symmetric <--> asymmetric keys are not permitted
POST /api/v3/crypto/resign Unsupported re-sign: symmetric <--> asymmetric keys are not permitted
POST /api/v3/crypto/stream/resign Unsupported re-sign: symmetric <--> asymmetric keys are not permitted

The detail names the two classes and no key. Identify the problem by its type (RFC 9457 §3.1.1), never by parsing detail (RFC 9457 §3.1.4).

The rule applies to a token addressed to or signed by one key, and to the one multi-key re-encryption it can still reach (a symmetric source recipient whose header declares an asymmetric alg, replaced by a SIMPLE asymmetric key). Every other symmetric key in a token addressed to or signed by several keys — an entry, newKid or a kept recipient or signer — is 422 unprocessable-entity with extensions.errorCode JOSE_SYMMETRIC_ACTOR, not this 400.

Common Causes

  • A symmetric source re-encrypted to an asymmetric key, or the reverse. The source key is the one the token's protected header names (kid and ank_kv, or sourceKid); the target is newKid.
  • A symmetric source re-signed with an asymmetric key, or the reverse. An HMAC signature cannot become an ECDSA, EdDSA, ML-DSA or composite signature by re-signing, and none of those can become an HMAC.
  • A composite key on one side and a symmetric key on the other. A composite key counts as asymmetric.

When it is decided

Every check below that applies to the request answers first; the key-class rule is decided after them, and before any key material is unwrapped or any byte of the file part is read:

  1. Authentication (401), the operation's scope (403) and the licence (402).
  2. Request shape and source-key selection. In a token addressed to or signed by several keys, a recipient or signer whose alg is symmetric or direct is refused here with 422 JOSE_SYMMETRIC_ACTOR, before any grant is checked. On /stream/resign, a validityDays outside its bounds is 400 invalid-input.
  3. Capability Grant (403) and key lifecycle (403).
  4. On /resign and /stream/resign: a sourceKid supplied for a composite source is 400 invalid-input; an unresolved or required-but-unavailable timestamping policy is 503; and on /resign, a COMPACT serialization requested under a mandated timestamp is 400 invalid-input.
  5. Key capability and keyOps (404, or 400 invalid-input for an operation the key does not support) — on every route, including the buffered re-encryption of one recipient of a JWE addressed to several keys.
  6. The existing 422 unprocessable-entity refusals, which keep precedence over the key-class rule:
    • in a token addressed to or signed by several keys, a composite recipient is JWE_COMPOSITE_RECIPIENT, and a symmetric newKid or kept recipient or signer is JOSE_SYMMETRIC_ACTOR. On a re-encryption of a JWE addressed to several keys these recipient checks are decided right after step 3, before the capability check of step 5;
    • on re-encryption, a JWE whose alg sits only in the unprotected recipient header is JWE_ALG_NOT_PROTECTED for a symmetric or composite key, and JWE_ALG_KEY_MISMATCH when that alg is not the key's own key-management algorithm. On the buffered re-encryption of one recipient of a JWE addressed to several keys this check is also decided before step 5.
  7. The key-class rule: 400 key-operation-incompatible.

On the streaming routes the refusal arrives before the response body starts, so there is no body part and no StreamVerdict.

Response Example

{
  "type": "https://docs.ankatech.co/errors/key-operation-incompatible",
  "title": "Key Operation Incompatible",
  "status": 400,
  "detail": "Unsupported re-encryption: symmetric <--> asymmetric keys are not permitted",
  "instance": "/api/v3/crypto/reencrypt",
  "correlationId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "timestamp": 1736936400,
  "extensions": null
}

Content-Type: application/problem+json — the response follows RFC 9457 Problem Details. correlationId is a top-level member, timestamp is in epoch seconds, and this problem carries no extension member, so extensions is null.

How to Resolve

  1. Re-encrypt or re-sign to a key of the same class as the source: symmetric to symmetric, or asymmetric (SIMPLE or composite) to asymmetric.
  2. To move a payload from a symmetric key to an asymmetric one, or the reverse, decrypt (or verify) it with the source key and then encrypt (or sign) it with the target key as two separate operations. This is not a re-encryption: it needs a DECRYPT (or VERIFY) grant on the source key and an ENCRYPT (or SIGN) grant on the target, and the plaintext reaches the caller in between.
  3. Read the key's metadata (GET the key) to confirm its kty before re-submitting: oct is symmetric.

For full schema definitions, examples, and interactive testing, see the Developer Hub Reference.