When I was dealing with some PNG images using NodeJS and
v2.6.1 as of the time of this post), I encountered a fatal error when reading some PNG files into memory:
load [filename] image error: Error: out of memorynode canvas fatal error: v8::tolocalchecked empty maybelocal.
I Googled this bug, and found an issue post) dating back to 2012, but it is not quite related to the error I encountered. As this error was not present for every single PNG that I had used as source, I decided to try another
npm image processor,
sharp, and see if this bug persisted. To my surprise, this error did not go away with the specific PNG file that caused problem for
sharp also threw an error.
sharp had failed with exactly the same file but not others, I figured that there might be an beyond the specific png file that I used. Thankfully,
sharp reported a more useful error message:
Intermittent Error: Input file has corrupt header.
It seems that this issue might be a problem with PNG’s headers. As I was working on a Mac, and this image was correctly displayed both by Preview.app and as Finder thumbnail, I wondered if this PNG was readable across platform, i.e., if it was encoded correctly.
After some Googling, I found a useful command line tool named
pngcheck, and I installed it with Homebrew:
brew install pngcheck
pngcheck was pretty straightforward to use, and as I wanted detailed output, I used
-t (“print contents of tEXt chunks”) and
-vv (“test very verbosely (decode & print line filters)”) options:
pngcheck -tvv artwork.png
The result was instantly showing the problem:
File: artwork.png (230109 bytes) chunk CgBI at offset 0x0000c, length 4: first chunk must be IHDR ERRORS DETECTED in artwork.png
Great, keywords discovered:
IHDR. It appears that this PNG that I was using did begin with an
IHDR header as the first chunk defined by PNG’s documentation.
Another quick Google search gave me the answer: according to this file format wiki, Apple uses a
CgBI header in its PNG conventions so as to optimize PNG display on its iOS devices. As this was not a standard header, the standard png library (
libpng) used by
sharp could not successfuly parse the Stream of
The work that remained was basically converting my original input file into a standard PNG – by trimming the CgBI binary chunk present in the file.
I found a handy piece of code (
npm package) named
cgbi-to-png that handles this conversion process nicely.
Note: Apparently, the conversion process done by
cgbi-to-png is synchronous.