inancgumus/learngo · error
not png
Error message
not png
What it means
transfer() reads the first pngSignLen bytes from the reader and requires them to equal the PNG file signature (\x89PNG\r\n\x1a\n). If the prefix does not match, the input is not a PNG image, so it refuses to continue and returns this error. This guards io.Copy downstream from treating non-PNG data as an image.
Source
Thrown at interfaces/16-io-compose/main.go:70
const pngSign = "\x89PNG\r\n\x1a\n"
const pngSignLen = 8
var memory bytes.Buffer
// limit what copy() reads.
// lr := io.LimitReader(r, pngSignLen)
// if n, err = io.Copy(&memory, lr); err != nil {
// return n, err
// }
// same as above. behind the scenes CopyN() calls LimitReader.
if n, err = io.CopyN(&memory, r, pngSignLen); err != nil {
return n, err
}
// check the png signature.
if !bytes.HasPrefix(memory.Bytes(), []byte(pngSign)) {
return n, errors.New("not png")
}
// stitch the PNG signature (memory) and the response body reader together.
// then copy them successively to the writer (*os.File).
return io.Copy(w, io.MultiReader(&memory, r))
}
View on GitHub (pinned to 3c475a78e5)
Solutions
- Verify the input source is actually a PNG (open it locally or check magic bytes before calling).
- Check the URL/endpoint returned 200 with image/png content, not an error page.
- If other formats should be accepted, remove or broaden the signature check in transfer.
- Handle the error at the caller and surface a clear 'input is not a PNG' message to the user.
Example fix
// before
if !bytes.HasPrefix(memory.Bytes(), []byte(pngSign)) {
return n, errors.New("not png")
}
// after
if !bytes.HasPrefix(memory.Bytes(), []byte(pngSign)) {
return n, fmt.Errorf("not png: got header %q", memory.Bytes()[:n])
} Defensive patterns
Strategy: validation
Validate before calling
func looksLikePNG(r io.Reader) (bool, error) {
header := make([]byte, 8)
if _, err := io.ReadFull(r, header); err != nil {
return false, err
}
return bytes.Equal(header, []byte("\x89PNG\r\n\x1a\n")), nil
} Type guard
func isPNGHeader(b []byte) bool {
return len(b) >= 8 && bytes.Equal(b[:8], []byte("\x89PNG\r\n\x1a\n"))
} Try / catch
if n, err := transfer(w, r); err != nil {
if err.Error() == "not png" {
http.Error(w, "input is not a PNG image", http.StatusUnsupportedMediaType)
return
}
return n, err
} Prevention
- Check Content-Type is image/png before feeding a download to transfer
- Sniff the first 8 magic bytes yourself before calling
- Do not trust file extensions; a .png URL may return an error page
- Use golang.org/x/image or http.DetectContentType to pre-validate
When it happens
Trigger: Calling transfer (via main) with a reader whose first bytes are not the 8-byte PNG signature — e.g. a JPEG/GIF file, an HTML error page, an empty body, or a text file.
Common situations: Downloading an image URL that returned a 404 HTML page instead of a PNG; piping the wrong local file; a server content-type mismatch where the extension says .png but the payload is another format.
Related errors
- invalid number
- record.domain cannot be empty
- record.page cannot be empty
- record.visits cannot be negative
- record.uniques cannot be negative
AI-assisted analysis of inancgumus/learngo@3c475a78e5 (2026-09-02).
Data as JSON: /api/errors/407a48fbcc037f45.
Report an issue: GitHub.