{"record":{"id":"2ddf74a2b891988e","repo":"gchq/CyberChef","slug":"unable-to-parse-jpeg-successfully","errorCode":null,"errorMessage":"Unable to parse JPEG successfully","messagePattern":"Unable to parse JPEG successfully","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/lib/FileSignatures.mjs","lineNumber":2722,"sourceCode":"            case 0x00: // Byte stuffing\n            case 0xd0: // Restart\n            case 0xd1: // Restart\n            case 0xd2: // Restart\n            case 0xd3: // Restart\n            case 0xd4: // Restart\n            case 0xd5: // Restart\n            case 0xd6: // Restart\n            case 0xd7: // Restart\n                stream.continueUntil(0xff);\n                break;\n\n            default:\n                stream.continueUntil(0xff);\n                break;\n        }\n    }\n\n    throw new Error(\"Unable to parse JPEG successfully\");\n}\n\n\n/**\n * GIF extractor.\n *\n * @param {Uint8Array} bytes\n * @param {Number} offset\n * @returns {Uint8Array}\n */\nexport function extractGIF(bytes, offset) {\n    const stream = new Stream(bytes.slice(offset));\n\n    // Move to application extension block.\n    stream.continueUntil([0x21, 0xff]);\n\n    // Move to Graphic Control Extension for frame #1.\n    stream.continueUntil([0x21, 0xf9]);","sourceCodeStart":2704,"sourceCodeEnd":2740,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/FileSignatures.mjs#L2704-L2740","documentation":"Thrown by extractJPEG after the marker-walking loop exits because the stream was exhausted without ever encountering the End-of-Image marker (0xFFD9, case 0xd9 which returns stream.carve()). It indicates a truncated or malformed JPEG that has no EOI. Like the other extractor throws this is a plain Error, not an OperationError.","triggerScenarios":"Feeding extractJPEG a JPEG whose EOI marker is missing (cut-off download, appended garbage, or a file terminated by a different marker); an offset that starts past the EOI; crafted data that loops until bytes run out.","commonSituations":"Incomplete file transfer; image intentionally missing EOI (some cameras/embedded tools); progressive JPEG whose scan data was truncated; data with embedded thumbnails where the outer stream ends early.","solutions":["Ensure the byte stream contains both SOI (0xFFD8) and a trailing EOI (0xFFD9).","Re-download or re-export the source image to obtain a complete JPEG.","Check the caller-supplied offset is not past the real EOI."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasJPEGEOI(bytes, offset = 0) {\n  for (let i = bytes.length - 2; i >= offset; i--) {\n    if (bytes[i] === 0xff && bytes[i + 1] === 0xd9) return true;\n  }\n  return false;\n}\nif (!looksLikeJPEG(bytes, offset) || !hasJPEGEOI(bytes, offset)) {\n  throw new Error(\"JPEG is truncated (missing EOI marker)\");\n}\nextractJPEG(bytes, offset);","typeGuard":"const hasCompleteJPEGFraming = (bytes, offset = 0) =>\n  bytes[offset] === 0xff && bytes[offset + 1] === 0xd8 &&\n  bytes[bytes.length - 2] === 0xff && bytes[bytes.length - 1] === 0xd9;","tryCatchPattern":"try {\n  extractJPEG(bytes, offset);\n} catch (err) {\n  if (/Unable to parse JPEG successfully/.test(err.message)) {\n    // truncated/missing EOI; re-acquire the file\n  } else throw err;\n}","preventionTips":["Ensure the byte stream includes both SOI (FFD8) and a trailing EOI (FFD9).","Re-download or re-export images that may have been truncated in transfer.","Validate framing markers before invoking the extractor."],"tags":["jpeg","file-format","truncation","parsing","file-signatures"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}