kovidgoyal/kitty · error

Failed to decode PNG image at

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.

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.

Appendix: 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)