{"record":{"id":"cd86e5a46a9af7ba","repo":"gohugoio/hugo","slug":"d-error-reading-blob-data-size-llu-read-z","errorCode":null,"errorMessage":"[%d]  Error reading blob data (size: %llu read: %zu) \\n","messagePattern":"\\[(.+?)\\]  Error reading blob data \\(size: %llu read: %zu\\) \\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/warpc/genavif/avif.c","lineNumber":341,"sourceCode":"        blob_size = *(uint32_t *)&blob_header[12];\n        blob_data = malloc((size_t)blob_size);\n        if (blob_data == NULL)\n        {\n            // Out of memory. Drain the blob from the input stream so the next\n            // command stays aligned, then report the error to the client instead\n            // of leaving the stream corrupted with no response.\n            drain_bytes(stream, (size_t)blob_size);\n            OutputMessage err_output = {0};\n            err_output.header = input.header;\n            snprintf(err_output.header.err, sizeof(err_output.header.err),\n                     \"out of memory allocating %u bytes for blob data\", blob_size);\n            write_output_message(&err_output);\n            goto cleanup;\n        }\n        read_bytes = fread(blob_data, 1, (size_t)blob_size, stream);\n        if (read_bytes != (size_t)blob_size)\n        {\n            fprintf(stderr, \"[%d]  Error reading blob data (size: %llu read: %zu) \\n\", blob_id, (unsigned long long)blob_size, read_bytes);\n            goto cleanup;\n        }\n\n        OutputMessage output = {0};\n        output.header = input.header;\n\n        if (strcmp(input.header.command, \"decode\") == 0)\n        {\n            avifDecoder *decoder = avifDecoderCreate();\n            if (decoder == NULL)\n            {\n                snprintf(output.header.err, sizeof(output.header.err), \"Failed to create AVIF decoder\");\n                write_output_message(&output);\n                goto cleanup;\n            }\n\n            decoder->ignoreExif = AVIF_TRUE;\n            decoder->ignoreXMP = AVIF_TRUE;","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/internal/warpc/genavif/avif.c#L323-L359","documentation":"Printed by genavif's handle_commands (avif.c:341) when the blob header declared `size` bytes but fread returned fewer. The message logs blob_id, declared size, and actual bytes read. The worker frees nothing extra and jumps to cleanup, leaving the partially-read bytes consumed. This is the canonical stream-desync signal: the host and worker disagree on payload length.","triggerScenarios":"The 16-byte header's size field does not match the bytes the host actually writes (off-by-one in stride*width*height, wrong pixel format byte count, endianness confusion when packing the size), the host closes the pipe partway through the blob, or a previous over-read advanced the stream past the true start of the blob.","commonSituations":"Re-encoding HDR/gain-map content where the Go side computed size with 4 bytes/pixel but the worker expected the size the host declared (mismatch after a depth change), animated AVIF frame counts differing between declared and actual, partial writes from a buffered writer that was flushed early, or test fixtures with a stale size field after the source image was edited.","solutions":["Compare the declared size against the actual byte count the Go side computes for the payload (width*height*bytesPerPixel*frameCount) and assert they match before writing the header.","Recompute size from the same source-of-truth used to allocate the pixel buffer, not from a cached field.","Check for partial writes: a Go io.Writer to a closed pipe can return (n<len, nil) on some platforms — treat any n != len as fatal.","If the worker is killed and respawned, ensure the new worker is not handed a half-drained pipe.","Log the declared size on the Go side at send time and correlate with the size printed here."],"exampleFix":"// before: size computed from cached stride that no longer matches the image\nsize := uint32(cachedStride * img.Bounds().Dy())\nhdr[12:] = size\n\n// after: compute from the actual bytes about to be written\npix := rgbaPixels(img)\nsize := uint32(len(pix))\nbinary.LittleEndian.PutUint32(hdr[12:], size)\nw.Write(hdr); w.Write(pix)","handlingStrategy":"validation","validationCode":"// Derive the blob size from the exact bytes you will send, not from a cached field.\nfunc blobHeader(id uint32, payload []byte) []byte {\n    h := make([]byte, 16)\n    copy(h[0:8], []byte(\"TAK35EM1\"))\n    binary.LittleEndian.PutUint32(h[8:12], id)\n    binary.LittleEndian.PutUint32(h[12:16], uint32(len(payload)))\n    return h\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always compute size = uint32(len(payload)) from the actual byte slice being sent.","For RGBA images, payload length must equal width*4*height (single frame) or width*4*height*frameCount (animated).","Treat any partial write as fatal and respawn the worker.","Log declared size at send time so it can be correlated with the worker's stderr print."],"tags":["rpc","io","protocol","avif","stream-desync","sizing"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}