ecdsa.com
CryptoJSOther tools

Error message

Malformed UTF-8 data

CryptoJS — thrown by .toString(CryptoJS.enc.Utf8) after decrypt

What it means

CryptoJS decrypted your ciphertext into bytes that are not valid UTF-8, and converting them to a string failed. Since CryptoJS's AES (CBC mode) has no integrity check, a wrong key does not fail the decryption — it just produces random bytes, and the UTF-8 decoder is the first thing to notice. The same wrong key can also yield an empty string instead of this error (when the padding check happens to fail cleanly), so both symptoms share one diagnosis.

Why it happens

How to fix it

  1. 1.

    Make key handling explicit and symmetric

    Skip the passphrase KDF ambiguity: pass a real key as a WordArray plus an explicit IV, identically on both sides.

    js
    const key = CryptoJS.enc.Hex.parse(hexKey);        // 32-byte key, both sides
    const iv  = CryptoJS.enc.Hex.parse(hexIv);         // 16-byte IV, both sides
    const ct  = CryptoJS.AES.encrypt(plaintext, key, { iv });
    const pt  = CryptoJS.AES.decrypt(ct.toString(), key, { iv })
                  .toString(CryptoJS.enc.Utf8);
  2. 2.

    Treat empty output as the same failure

    Check the result and surface a real error — silent empty strings hide wrong-key bugs for months.

    js
    const pt = CryptoJS.AES.decrypt(ct, key, { iv }).toString(CryptoJS.enc.Utf8);
    if (!pt) throw new Error("decryption failed: wrong key/IV or corrupted ciphertext");
  3. 3.

    Prefer an authenticated scheme

    WebCrypto's AES-GCM authenticates ciphertext: a wrong key fails loudly and deterministically instead of producing garbage — eliminating this error class entirely.

    js
    const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ct);
    // wrong key → OperationError, never silent garbage

Related errors

← Browse the full signature error database