ecdsa.com
WebCrypto crypto.subtleWebCrypto

Error message

OperationError: The operation failed for an operation-specific reason

WebCrypto crypto.subtle — browsers & Node

What it means

The deliberately vague catch-all of WebCrypto: the primitive itself failed. For decrypt() with AES-GCM it means the authentication tag did not verify — wrong key, wrong IV, wrong additional data, or modified ciphertext. The spec keeps the message generic so implementations do not leak why a cryptographic check failed. For sign() and deriveBits() the same name covers internal primitive failures, so the operation in your stack trace matters more than the message text.

Why it happens

How to fix it

  1. 1.

    Ship IV and ciphertext together, decode symmetrically

    Prepend the IV to the ciphertext and split on decrypt, so both sides always agree; use one base64 flavor everywhere.

    js
    // encrypt
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
    const packed = new Uint8Array([...iv, ...new Uint8Array(ct)]);
    
    // decrypt
    const buf = packedFromWire;
    const pt = await crypto.subtle.decrypt(
      { name: "AES-GCM", iv: buf.slice(0, 12) }, key, buf.slice(12),
    );
  2. 2.

    Treat it as an integrity verdict, not corruption

    When decrypt throws OperationError, the correct response is "this ciphertext is not authentic under this key" — reject it. Do not retry with padding tweaks or fall back to unauthenticated modes; the check failing is the security feature.

  3. 3.

    Compare key fingerprints across the two sides

    Export both keys and hash them; differing digests end the mystery in one step.

    js
    const raw = await crypto.subtle.exportKey("raw", key);
    const fp = await crypto.subtle.digest("SHA-256", raw);
    console.log(Buffer.from(fp).toString("hex").slice(0, 16)); // compare on both sides

Related errors

← Browse the full signature error database