Fix it now
- Drop the file
- Repair runs locally
- Download the result
When a viewer, pngcheck, libpng or ImageMagick rejects a PNG with a "CRC error" — most often libpng error: IDAT: CRC error — it means a chunk's bytes no longer match the checksum stored beside them. The file changed after it was written. Drop the .png above and the tool parses the chunk structure in your browser, recomputes every CRC-32 to find which chunk actually broke, and rebuilds a valid file around whatever survived — telling you honestly whether your case is the deterministic, zero-loss kind or the partial kind, without the image ever leaving your device.
What a PNG "CRC error" actually is
A PNG begins with a fixed 8-byte signature — the bytes 89 50 4E 47 0D 0A 1A 0A — and everything after it is a chain of chunks. Each chunk is laid out identically: a 4-byte big-endian length (counting the data only), a 4-byte ASCII type such as IHDR, IDAT or IEND, the data itself, and a trailing 4-byte CRC-32. That checksum is computed over the chunk's type and data — not its length — using the standard ISO-3309 / ITU-T V.42 polynomial (reflected form 0xEDB88320). A "CRC error" means a decoder recomputed that value and it did not equal the four bytes on disk: proof the chunk's bytes changed since the file was written.
Tools phrase it in their own way. pngcheck prints CRC error in chunk IDAT (computed 12ab34cd, expected 89ef01ab); libpng raises libpng error: IDAT: CRC error or the blunter Read Error; ImageMagick surfaces the zlib layer underneath as IDAT: invalid distance too far back; a browser simply draws nothing. The chunk named in the message tells you the stakes. A CRC error on an ancillary chunk — tEXt, gAMA, pHYs, sRGB, whose type starts with a lowercase letter — is cosmetic and the image still decodes. A CRC error on a critical chunk (the four whose type starts uppercase: IHDR, PLTE, IDAT, IEND) is what actually stops the file from opening.
The three chunks that carry the picture are IHDR, IDAT and IEND. IHDR is exactly 13 bytes: width and height (4 bytes each), then bit depth, colour type (0 greyscale, 2 truecolour, 3 indexed, 4 grey+alpha, 6 RGBA), compression method, filter method and interlace method. IDAT holds the compressed pixels and is frequently split across several consecutive chunks that must be concatenated before decompression. IEND is an empty, zero-length marker that says the file is complete. The tool reads this structure locally, checks every CRC, and identifies the chunk at fault before it changes a single byte.
The damage a PNG can undo exactly
Because every chunk carries its own checksum, a whole class of PNG damage has a deterministic fix — the correct answer is knowable from the maths, not guessed.
A wrong CRC over intact data. The gentlest failure: the chunk's data is byte-for-byte correct but its stored CRC-32 is stale — a common result of an editor that rewrote the data without updating the checksum, or a single flipped bit inside the 4-byte CRC field itself. Recomputing the CRC from the type and data and writing it back makes the file valid again with zero loss. The checksum was the only thing wrong.
Zeroed or scrambled dimensions. If the width or height in IHDR is corrupted, no decoder can allocate a canvas and the file won't open — often as libpng error: Invalid IHDR data. But IHDR stores its own CRC, computed back when the dimensions were correct. That turns recovery into a search: try candidate width/height pairs, recompute the 13-byte chunk's CRC-32 for each, and stop when it equals the stored value. The original dimensions fall straight out of the arithmetic, exactly, with no guessing.
Newline and high-bit mangling. The 8-byte signature was designed as a tripwire for text-mode corruption: the 0D 0A pair catches a CRLF→LF translation, the trailing 0A catches LF→CRLF, and the leading 89 (high bit set) catches a 7-bit transfer that stripped it. When a PNG was pushed through an FTP client in ASCII mode, or a script that rewrote it as text, the substitution is consistent and reversible — undo the transform and the original bytes return throughout the file, not just in the signature.
IDAT, deflate, and where recovery stops
Inside the IDAT chunks the pixels are a single zlib datastream: a 2-byte header (commonly 78 9C), a deflate-compressed body, and a 4-byte Adler-32 checksum at the very end. Before compression, each scanline is prefixed with a 1-byte filter type (0 None, 1 Sub, 2 Up, 3 Average, 4 Paeth) that predicts each pixel from its neighbours. The rows are written top to bottom, and that single fact decides what is recoverable once the data itself — not just a checksum — is damaged.
Truncation. The most common break: a download that stopped early, a copy interrupted when a drive was pulled, a crash mid-write. The front of the file is intact and the tail — usually including IEND and the Adler-32 — is simply absent, so decoders report EOF while reading IDAT or zlib's incorrect data check. Because deflate decodes top to bottom, the tool salvages every complete scanline up to the cut, writes a fresh valid IEND, and hands you an honest partial: your real pixels, as far down as the bytes reach.
Mid-stream corruption. When the damage lands inside the deflate body rather than at the end, the limit is hard. Deflate is one continuous stream with no periodic resync markers, so once inflate hits a bad byte it loses the thread — you see invalid distance too far back, invalid literal/length code or invalid block type — and everything after that point decodes as noise. A single corrupted byte partway down can cost every row beneath it, not just one line. The tool keeps the rows that decoded cleanly above the wound; it cannot stitch the stream back together below it, and an interlaced (Adam7) file additionally loses the later passes that would have sharpened the lower image.
Two honest non-starters. A file that is 0 bytes or all zeros has no image to rebuild — that is a data-recovery problem for the storage, not a repair. And when a chunk's data (not merely its CRC) was overwritten, the checksum can prove the damage but never reverse it. Everything above runs entirely in your browser tab — the parser is dependency-free TypeScript — so a PNG that might be a private photo, a screenshot or a scanned document is never copied to a server: open the Network tab and confirm 0 bytes leave.
What this can and can't fix
Can fix
- A chunk whose stored CRC-32 no longer matches its type+data while the bytes are intact — recomputed and rewritten with zero loss
- A PNG with zeroed or scrambled IHDR width/height — the original dimensions are brute-forced back from the stored IHDR CRC
- Files mangled by CRLF↔LF newline translation or high-bit stripping in a text-mode transfer — the reversible substitution is undone
- A truncated PNG missing its tail and IEND — the top scanlines that decoded are salvaged and a valid IEND is written
- Rows above a mid-stream IDAT hit — everything deflate decoded cleanly before the corrupted byte is kept
Can't fix
- Rows below a mid-stream IDAT corruption — deflate cannot resynchronise, so everything after the damaged byte decodes as noise
- Scanlines past the cut in a truncated file — those bytes were never written to disk
- A file that is 0 bytes or all zeros — there is no image to rebuild; that is a data-recovery problem, not a repair
- The exact colours of a chunk whose data (not just its CRC) was overwritten — the CRC only proves damage, it can't reverse it
- The later interlaced (Adam7) passes below a mid-stream break — the detail they add is gone
If a repair fails, we tell you why (missing data versus broken structure), and you are never charged for a failed repair.