PNG "CRC error" and IDAT damage

A PNG audits itself: every chunk ends in a CRC-32, so the format can point to its own damage with rare precision. That same structure draws a hard line — a wrong checksum over good data is a lossless fix, while a byte lost inside the IDAT deflate stream takes every row beneath it. Sorting your file into the right bucket does not require uploading it anywhere.

Your file never leaves your device — repair runs in your browser. 0 bytes uploaded.

Your data is the big block — usually intact. What breaks is the small index. Repair rebuilds it.

Drop a file here or browseTap to choose a file

ZIP · Office · PDF · video · JPG · PNG · RAR/7z · SQLite — repaired right here in your browser. Nothing is uploaded

0 bytes uploadedFree: 3 repairs/day — up to 500 MB video, 100 MB docs & archives, 50 MB photos. Free download. Single repair €5.90. No account required.

Fix it now

  1. Drop the file
  2. Repair runs locally
  3. 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.

FAQ

Does a PNG "CRC error" mean my image is lost?

Not necessarily. The CRC-32 on each chunk only tells you that chunk's bytes changed since the file was written. If just the checksum is wrong and the data is intact, recomputing it fixes the file with zero loss. If the data itself changed, how much you recover depends on which chunk was hit — a bad ancillary chunk like tEXt is cosmetic, whereas damage inside IDAT can cost the rows below it.

What is IDAT, and why does damage there lose the bottom of the image?

IDAT holds the compressed pixels as one continuous deflate stream written top to bottom. Deflate has no periodic markers to resynchronise on, so once the decoder hits a corrupted byte it loses the thread and everything after it decodes as noise. That is why a single bad byte partway down can take every row beneath it, while a clean truncation at least leaves the top intact.

My PNG opens in one program but shows a CRC error in another — why?

Many lenient viewers ignore CRC mismatches and render the file anyway, while strict tools like pngcheck or libpng refuse it. Seeing it open somewhere usually means the pixel data is fine and only the checksum is stale — the deterministic, lossless case. Recomputing the CRC produces a file that every strict decoder will accept.

Can zeroed image dimensions (IHDR) really be recovered exactly?

Yes, in most cases. The IHDR chunk carries a CRC-32 computed while the width and height were still correct. Recovery tries candidate dimensions, recomputes the chunk's CRC for each, and stops when it matches the stored value — so the original size is derived from the maths, not guessed.

Is my PNG uploaded to be repaired?

No. The file is read from your disk and rebuilt in your browser tab; the parser is plain TypeScript and nothing is transmitted. You can open the Network tab and confirm 0 bytes leave — which matters when the PNG is a private photo, a screenshot of something sensitive, or a scanned document.

Related: Repair a JPEG · PNG won't open: chunks, CRCs & what's recoverable · "Unexpected end of archive" (deflate truncation) · Verify the zero-upload claim