{"record":{"id":"bf241a3efe971269","repo":"mozilla/pdf.js","slug":"invalid-key-length","errorCode":null,"errorMessage":"invalid key length","messagePattern":"invalid key length","errorType":"exception","errorClass":"FormatError","httpStatus":null,"severity":"error","filePath":"src/core/crypto.js","lineNumber":1101,"sourceCode":"        keyLength = 40;\n      } else {\n        // Trying to find default handler -- it usually has Length.\n        const cfDict = dict.get(\"CF\");\n        const streamCryptoName = dict.get(\"StmF\");\n        if (cfDict instanceof Dict && streamCryptoName instanceof Name) {\n          cfDict.suppressEncryption = true; // See comment below.\n          const handlerDict = cfDict.get(streamCryptoName.name);\n          keyLength = handlerDict?.get(\"Length\") || 128;\n          if (keyLength < 40) {\n            // Sometimes it's incorrect value of bits, generators specify\n            // bytes.\n            keyLength <<= 3;\n          }\n        }\n      }\n    }\n    if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) {\n      throw new FormatError(\"invalid key length\");\n    }\n\n    let cf = null;\n    let stmf = Name.get(\"Identity\");\n    let strf = Name.get(\"Identity\");\n    let eff = stmf;\n\n    if (algorithm >= 4) {\n      cf = dict.get(\"CF\");\n      if (cf instanceof Dict) {\n        // The 'CF' dictionary itself should not be encrypted, and by setting\n        // `suppressEncryption` we can prevent an infinite loop inside of\n        // `XRef_fetchUncompressed` if the dictionary contains indirect\n        // objects (fixes issue7665.pdf).\n        cf.suppressEncryption = true;\n      }\n      stmf = dict.get(\"StmF\") || Name.get(\"Identity\");\n      strf = dict.get(\"StrF\") || Name.get(\"Identity\");","sourceCodeStart":1083,"sourceCodeEnd":1119,"githubUrl":"https://github.com/mozilla/pdf.js/blob/5903d58d58e4dd9ce6ffa3834aea8480f06b4ada/src/core/crypto.js#L1083-L1119","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Re-save the PDF with Acrobat or qpdf so the Encrypt dict carries a valid Length (e.g., 128 or 256).","If generating PDFs, ensure Length is in bits and a multiple of 8 between 40 and 256.","Pre-validate and reject malformed PDFs with a clear message."],"exampleFix":"// before\nconst doc = await getDocument({ url }).promise;\n\n// after\ntry {\n  const doc = await getDocument({ url }).promise;\n} catch (e) {\n  if (e.message === 'invalid key length') {\n    throw new Error('Encryption key length is malformed; re-save the PDF with a supported producer.');\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const doc = await getDocument({ url }).promise;\n} catch (e) {\n  if (e.name === 'FormatError' && e.message === 'invalid key length') {\n    notifyUser('Encryption key length is malformed; please re-save the PDF.');\n    return;\n  }\n  throw e;\n}","preventionTips":["Validate producer output with qpdf --check before publishing.","Reject PDFs whose Encrypt dict Length is non-standard at ingestion.","Keep a repair step (qpdf --encrypt re-encryption) in your ingestion pipeline."],"tags":["encryption","pdf-security","format-error","validation"],"backgroundTag":null,"analyzedSha":"5903d58d58e4dd9ce6ffa3834aea8480f06b4ada","analyzedAt":"2026-08-13T02:28:27.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}