gohugoio/hugo · error

[%d] Error reading blob data (size: %llu read: %zu) \n

Error message

[%d]  Error reading blob data (size: %llu read: %zu) \n

What it means

Printed by genwebp's handle_commands (webp.c:593) when the blob header's declared size does not match the bytes fread returned. Logs blob_id, declared size, and actual read count. Identical semantics to the genavif counterpart (823): the canonical stream-desync signal for genwebp.

Source

Thrown at internal/warpc/genwebp/webp.c:593

        blob_size = *(uint32_t *)&blob_header[12];
        blob_data = malloc((size_t)blob_size);
        if (blob_data == NULL)
        {
            // Out of memory. Drain the blob from the input stream so the next
            // command stays aligned, then report the error to the client instead
            // of leaving the stream corrupted with no response.
            drain_bytes(stream, (size_t)blob_size);
            OutputMessage err_output = {0};
            err_output.header = input.header;
            snprintf(err_output.header.err, sizeof(err_output.header.err),
                     "out of memory allocating %u bytes for blob data", blob_size);
            write_output_message(&err_output);
            goto cleanup;
        }
        read_bytes = fread(blob_data, 1, (size_t)blob_size, stream);
        if (read_bytes != (size_t)blob_size)
        {
            fprintf(stderr, "[%d]  Error reading blob data (size: %llu read: %zu) \n", blob_id, (unsigned long long)blob_size, read_bytes);
            goto cleanup;
        }

        OutputMessage output = {0};
        output.header = input.header;

        if (strcmp(input.header.command, "decode") == 0)
        {

            WebPData data;
            data.bytes = blob_data;
            data.size = (size_t)blob_size;

            WebPDecoderConfig config;
            if (!initDecoderConfig(&config, data))
            {
                strncpy(output.header.err, "Failed to initialize WebPDecoderConfig", sizeof(output.header.err) - 1);
                write_output_message(&output);

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Compute size from the actual byte length of the payload about to be written: size := uint32(len(pix)).
  2. For animated encodes, size = stride*height*frameCount, with stride = width*4 for RGBA.
  3. Treat any partial write (n != len) from the Go writer as fatal.
  4. On Go-side errors mid-send, close and respawn the worker rather than reusing a half-drained pipe.
  5. Log the declared size at send time and correlate with the size printed here.

Example fix

// before: size hardcoded / cached, mismatches actual payload
size := uint32(1024 * 1024) // stale
binary.LittleEndian.PutUint32(hdr[12:], size)

// after
pix := flattenRGBA(frames, w, h, frameCount)
size := uint32(len(pix))
binary.LittleEndian.PutUint32(hdr[12:], size)
w.Write(hdr); w.Write(pix)
Defensive patterns

Strategy: validation

Validate before calling

func webpBlobHeader(id uint32, payload []byte) []byte {
    h := make([]byte, 16)
    copy(h[0:8], []byte("TAK35EM1"))
    binary.LittleEndian.PutUint32(h[8:12], id)
    binary.LittleEndian.PutUint32(h[12:16], uint32(len(payload)))
    return h
}

Prevention

When it happens

Trigger: Header size field disagrees with the bytes the host wrote (wrong stride*width*height, wrong bytes-per-pixel for has_alpha, endianness packing error), host closed the pipe partway through the blob, or a previous over-read advanced the stream past the true blob start.

Common situations: RGB vs RGBA byte-count mismatch (3 vs 4 bytes/pixel), animated frame count differing from declared, partial write from a flushed buffered writer, stale cached size after the source image changed, or test fixtures with a hardcoded size field.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/8fb36d4912e24af7. Report an issue: GitHub.