jgraph/drawio-desktop · error · Error

PNGImageDecoder1

Error message

PNGImageDecoder1

What it means

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).

Source

Thrown at src/main/electron.js:1991

		var magic1 = origBuff.readUInt32BE(inOffset);
		inOffset += 4;
		var magic2 = origBuff.readUInt32BE(inOffset);
		inOffset += 4;
		
		if (magic1 != 0x89504e47 && magic2 != 0x0d0a1a0a)
		{
			throw new Error("PNGImageDecoder0");
		}
		
		outBuff.writeUInt32BE(magic1, outOffset);
		outOffset += 4;
		outBuff.writeUInt32BE(magic2, outOffset);
		outOffset += 4;
	}
	catch (e)
	{
		log.error(e.message, {stack: e.stack});
		throw new Error("PNGImageDecoder1");
	}

	try
	{
		while (inOffset < origBuff.length)
		{
			var length = origBuff.readInt32BE(inOffset);
			inOffset += 4;
			var type = origBuff.readInt32BE(inOffset)
			inOffset += 4;

			if (type == PNG_CHUNK_IDAT)
			{
				// Insert zTXt chunk before IDAT chunk
				outBuff.writeInt32BE(dataLen, outOffset);
				outOffset += 4;

				var typeSignature = isDpi? 'pHYs' : (compressed ? "zTXt" : "tEXt");

View on GitHub (pinned to 403a2cb79f)

Solutions

  1. Confirm the buffer passed to writePngWithText starts with the PNG signature 89 50 4E 47 0D 0A 1A 0A before calling it.
  2. If invoking through the export IPC, verify args.format==='png' and that nativeImage.toPNG() returned a non-empty Buffer (check buf.length >= 8).
  3. On Linux CI/container builds, install the Electron runtime dependencies (libpng, libgtk, etc.) so nativeImage rendering produces a real PNG.
  4. Inspect the log.error line emitted just before the throw — it carries the original error message and stack pinpointing the real cause.

Example fix

// before
var data = img.toPNG();
data = writePngWithText(data, 'dpi', args.dpi);

// after
var data = img.toPNG();
if (!Buffer.isBuffer(data) || data.length < 8 ||
	data.readUInt32BE(0) !== 0x89504e47 || data.readUInt32BE(4) !== 0x0d0a1a0a) {
	throw new Error('PNG export produced an invalid PNG buffer');
}
data = writePngWithText(data, 'dpi', args.dpi);
Defensive patterns

Strategy: validation

Validate before calling

function isValidPng(buf) {
	return Buffer.isBuffer(buf) && buf.length >= 8 &&
		buf.readUInt32BE(0) === 0x89504e47 &&
		buf.readUInt32BE(4) === 0x0d0a1a0a;
}
// usage
if (!isValidPng(data)) throw new Error('not a PNG');
writePngWithText(data, 'dpi', args.dpi);

Type guard

function isValidPng(buf) {
	return Buffer.isBuffer(buf) && buf.length >= 8 &&
		buf.readUInt32BE(0) === 0x89504e47 &&
		buf.readUInt32BE(4) === 0x0d0a1a0a;
}

Try / catch

try { writePngWithText(data, 'dpi', args.dpi); }
catch (e) {
	if (e.message === 'PNGImageDecoder1') {
		log.error('PNG embed failed; falling back to unmodified PNG', data && data.length);
		return data; // degrade to plain PNG
	}
	throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.


AI-assisted analysis of jgraph/drawio-desktop@403a2cb79f (2026-08-13). Data as JSON: /api/errors/aa8f930dd04b0e5b. Report an issue: GitHub.