kovidgoyal/kitty · error

Failed to decode PNG image at: %s with error: %s

Error message

Failed to decode PNG image at: %s with error: %s

What it means

png_from_data failed to decode a PNG: the zlib inflate or libpng read pipeline reported an error (captured in d.error.buf). It is logged with the path/data source given by the caller (window icon, background image, or window logo), and the function returns false so the caller falls back to defaults.

Source

Thrown at kitty/graphics.c:514

}

static void
print_png_read_error(png_read_data *d, const char *code, const char *msg) {
    if (d->error.used >= d->error.capacity) {
        size_t cap = MAX(2 * d->error.capacity, 1024 + d->error.used);
        d->error.buf = realloc(d->error.buf, cap);
        if (!d->error.buf) return;
        d->error.capacity = cap;
    }
    d->error.used += snprintf(d->error.buf + d->error.used, d->error.capacity - d->error.used, "%s: %s ", code, msg);
}

bool
png_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t **data, unsigned int *width, unsigned int *height, size_t *sz) {
    png_read_data d = {.err_handler = print_png_read_error};
    inflate_png_inner(&d, png_data, png_data_sz, MAX_IMAGE_DIMENSION);
    if (!d.ok) {
        log_error("Failed to decode PNG image at: %s with error: %s", path_for_error_messages, d.error.used > 0 ? d.error.buf : "");
        free(d.decompressed);
        free(d.row_pointers);
        free(d.error.buf);
        return false;
    }
    *data = d.decompressed;
    free(d.row_pointers);
    free(d.error.buf);
    *sz = d.sz;
    *height = d.height;
    *width = d.width;
    return true;
}

bool
png_from_file_pointer(FILE *fp, const char *path_for_error_messages, uint8_t **data, unsigned int *width, unsigned int *height, size_t *sz) {
    size_t capacity = 16 * 1024, pos = 0;
    unsigned char *buf = malloc(capacity);

View on GitHub (pinned to 6d5d0c4406)

Solutions

  1. Validate the image is a real PNG: file icon.png (or pngcheck icon.png)
  2. Re-export/re-download the image; open it in an image editor and re-save as PNG
  3. Resize the image below kitty's MAX_IMAGE_DIMENSION
  4. If passing raw data programmatically, confirm you are passing PNG-encoded bytes, not decoded RGBA

Example fix

# before (kitty.conf)
background_image ~/pics/photo.cwebp
# after
background_image ~/pics/photo.png
Defensive patterns

Strategy: validation

Validate before calling

import struct

def is_png(data: bytes) -> bool:
    return len(data) >= 8 and data[:8] == b'\x89PNG\r\n\x1a\n'

# and: pngcheck file.png before configuring it

Type guard

def is_valid_png(data: bytes) -> bool:
    if not is_png(data):
        return False
    w, h = struct.unpack('>II', data[16:24])
    return 0 < w <= MAX_IMAGE_DIMENSION and 0 < h <= MAX_IMAGE_DIMENSION

Try / catch

if not png_from_data(buf, sz, path, &out, &w, &h, &osz) { /* keep default icon/background instead of failing */ }

Prevention

When it happens

Trigger: Passing corrupt or non-PNG bytes to kitty's graphics API: set_os_window_icon with bad data, background_image config pointing to a truncated/corrupt PNG, or a window logo image that exceeds MAX_IMAGE_DIMENSION.

Common situations: background_image in kitty.conf pointing to a partially-downloaded or CMYK/16-bit-unusual PNG; icons damaged by transfer; images larger than kitty's MAX_IMAGE_DIMENSION limit; providing JPEG/WebP bytes where PNG is required.

Understand the failure class

Related errors


AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27). Data as JSON: /api/errors/55d53a5a93f5f3d0. Report an issue: GitHub.