mozilla/pdf.js · error · FormatError

invalid key length

Error message

invalid key length

What it means

Thrown after recovery attempts when the resolved key length is not an integer, is below 40 bits, or is not a multiple of 8. pdf.js tries the Encrypt dict's Length, falls back to 40 for V<=3, and probes the default crypt filter (CF/StmF) for V>=4—if none yields a valid value, the key length is unusable.

Source

Thrown at src/core/crypto.js:1101

        keyLength = 40;
      } else {
        // Trying to find default handler -- it usually has Length.
        const cfDict = dict.get("CF");
        const streamCryptoName = dict.get("StmF");
        if (cfDict instanceof Dict && streamCryptoName instanceof Name) {
          cfDict.suppressEncryption = true; // See comment below.
          const handlerDict = cfDict.get(streamCryptoName.name);
          keyLength = handlerDict?.get("Length") || 128;
          if (keyLength < 40) {
            // Sometimes it's incorrect value of bits, generators specify
            // bytes.
            keyLength <<= 3;
          }
        }
      }
    }
    if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) {
      throw new FormatError("invalid key length");
    }

    let cf = null;
    let stmf = Name.get("Identity");
    let strf = Name.get("Identity");
    let eff = stmf;

    if (algorithm >= 4) {
      cf = dict.get("CF");
      if (cf instanceof Dict) {
        // The 'CF' dictionary itself should not be encrypted, and by setting
        // `suppressEncryption` we can prevent an infinite loop inside of
        // `XRef_fetchUncompressed` if the dictionary contains indirect
        // objects (fixes issue7665.pdf).
        cf.suppressEncryption = true;
      }
      stmf = dict.get("StmF") || Name.get("Identity");
      strf = dict.get("StrF") || Name.get("Identity");

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Re-save the PDF with Acrobat or qpdf so the Encrypt dict carries a valid Length (e.g., 128 or 256).
  2. If generating PDFs, ensure Length is in bits and a multiple of 8 between 40 and 256.
  3. Pre-validate and reject malformed PDFs with a clear message.

Example fix

// before
const doc = await getDocument({ url }).promise;

// after
try {
  const doc = await getDocument({ url }).promise;
} catch (e) {
  if (e.message === 'invalid key length') {
    throw new Error('Encryption key length is malformed; re-save the PDF with a supported producer.');
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const doc = await getDocument({ url }).promise;
} catch (e) {
  if (e.name === 'FormatError' && e.message === 'invalid key length') {
    notifyUser('Encryption key length is malformed; please re-save the PDF.');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Encrypt dict with a fractional or absurdly small Length entry; a CF default handler whose Length is non-numeric; a producer that emitted Length in bytes that fell outside the recovery heuristic (the code shifts <<3 only when <40).

Common situations: Buggy or hand-rolled PDF generators that write Length as bytes (e.g., 16) below the recovery threshold, or as a non-integer; corrupt Encrypt dict.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/bf241a3efe971269. Report an issue: GitHub.