{"record":{"id":"aa8f930dd04b0e5b","repo":"jgraph/drawio-desktop","slug":"pngimagedecoder1","errorCode":null,"errorMessage":"PNGImageDecoder1","messagePattern":"PNGImageDecoder1","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/electron.js","lineNumber":1991,"sourceCode":"\t\tvar magic1 = origBuff.readUInt32BE(inOffset);\n\t\tinOffset += 4;\n\t\tvar magic2 = origBuff.readUInt32BE(inOffset);\n\t\tinOffset += 4;\n\t\t\n\t\tif (magic1 != 0x89504e47 && magic2 != 0x0d0a1a0a)\n\t\t{\n\t\t\tthrow new Error(\"PNGImageDecoder0\");\n\t\t}\n\t\t\n\t\toutBuff.writeUInt32BE(magic1, outOffset);\n\t\toutOffset += 4;\n\t\toutBuff.writeUInt32BE(magic2, outOffset);\n\t\toutOffset += 4;\n\t}\n\tcatch (e)\n\t{\n\t\tlog.error(e.message, {stack: e.stack});\n\t\tthrow new Error(\"PNGImageDecoder1\");\n\t}\n\n\ttry\n\t{\n\t\twhile (inOffset < origBuff.length)\n\t\t{\n\t\t\tvar length = origBuff.readInt32BE(inOffset);\n\t\t\tinOffset += 4;\n\t\t\tvar type = origBuff.readInt32BE(inOffset)\n\t\t\tinOffset += 4;\n\n\t\t\tif (type == PNG_CHUNK_IDAT)\n\t\t\t{\n\t\t\t\t// Insert zTXt chunk before IDAT chunk\n\t\t\t\toutBuff.writeInt32BE(dataLen, outOffset);\n\t\t\t\toutOffset += 4;\n\n\t\t\t\tvar typeSignature = isDpi? 'pHYs' : (compressed ? \"zTXt\" : \"tEXt\");","sourceCodeStart":1973,"sourceCodeEnd":2009,"githubUrl":"https://github.com/jgraph/drawio-desktop/blob/403a2cb79f431db13a395f033b67eeebc1631754/src/main/electron.js#L1973-L2009","documentation":"Thrown by the catch block guarding the PNG signature read in writePngWithText (src/main/electron.js:1971-1992). The inner try reads the first 8 bytes of origBuff and validates the PNG magic (0x89504e47 / 0x0d0a1a0a, throwing the sentinel 'PNGImageDecoder0' on mismatch); any failure there — short buffer, non-Buffer, or bad magic — is logged via log.error and re-thrown as the opaque 'PNGImageDecoder1'. This is the only entry point that embeds dpi or mxGraphModel XML into an exported PNG (lines 2746, 2753).","triggerScenarios":"Export to PNG with args.dpi set, or args.embedXml==\"1\" with an mxGraphModel payload, when img.toPNG() returned a buffer that is not a valid 8-byte PNG (truncated, JPEG/PNG mismatch, nativeImage fallback to empty buffer on a headless/minimal Electron build).","commonSituations":"Running image export in an environment where nativeImage produced no real PNG (missing libpng, headless Linux without the right Electron deps); a custom caller passing a pre-encoded non-PNG buffer into writePngWithText; a regression where args.format was 'png' but toPNG() silently returned empty.","solutions":["Confirm the buffer passed to writePngWithText starts with the PNG signature 89 50 4E 47 0D 0A 1A 0A before calling it.","If invoking through the export IPC, verify args.format==='png' and that nativeImage.toPNG() returned a non-empty Buffer (check buf.length >= 8).","On Linux CI/container builds, install the Electron runtime dependencies (libpng, libgtk, etc.) so nativeImage rendering produces a real PNG.","Inspect the log.error line emitted just before the throw — it carries the original error message and stack pinpointing the real cause."],"exampleFix":"// before\nvar data = img.toPNG();\ndata = writePngWithText(data, 'dpi', args.dpi);\n\n// after\nvar data = img.toPNG();\nif (!Buffer.isBuffer(data) || data.length < 8 ||\n\tdata.readUInt32BE(0) !== 0x89504e47 || data.readUInt32BE(4) !== 0x0d0a1a0a) {\n\tthrow new Error('PNG export produced an invalid PNG buffer');\n}\ndata = writePngWithText(data, 'dpi', args.dpi);","handlingStrategy":"validation","validationCode":"function isValidPng(buf) {\n\treturn Buffer.isBuffer(buf) && buf.length >= 8 &&\n\t\tbuf.readUInt32BE(0) === 0x89504e47 &&\n\t\tbuf.readUInt32BE(4) === 0x0d0a1a0a;\n}\n// usage\nif (!isValidPng(data)) throw new Error('not a PNG');\nwritePngWithText(data, 'dpi', args.dpi);","typeGuard":"function isValidPng(buf) {\n\treturn Buffer.isBuffer(buf) && buf.length >= 8 &&\n\t\tbuf.readUInt32BE(0) === 0x89504e47 &&\n\t\tbuf.readUInt32BE(4) === 0x0d0a1a0a;\n}","tryCatchPattern":"try { writePngWithText(data, 'dpi', args.dpi); }\ncatch (e) {\n\tif (e.message === 'PNGImageDecoder1') {\n\t\tlog.error('PNG embed failed; falling back to unmodified PNG', data && data.length);\n\t\treturn data; // degrade to plain PNG\n\t}\n\tthrow e;\n}","preventionTips":["Always validate the PNG signature before calling writePngWithText.","On Linux/container Electron, install nativeImage runtime deps so toPNG() returns real bytes.","Inspect log.error output — it carries the original inner error before the opaque PNGImageDecoder1 re-throw."],"tags":["png","image-export","nativeimage","encoding"],"backgroundTag":null,"analyzedSha":"403a2cb79f431db13a395f033b67eeebc1631754","analyzedAt":"2026-08-13T00:15:02.468Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}