{"record":{"id":"07031f48822714c5","repo":"gohugoio/hugo","slug":"error-reading-blob-header-n-07031f","errorCode":null,"errorMessage":"Error reading blob header\\n","messagePattern":"Error reading blob header\\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/warpc/genwebp/webp.c","lineNumber":571,"sourceCode":"        uint32_t blob_size = 0;\n\n        // Remove newline character if present\n        line[strcspn(line, \"\\n\")] = 0;\n\n        if (strlen(line) == 0)\n        {\n            continue;\n        }\n\n        input = parse_input_message(line);\n\n        // Next in stream is a blob header defined in https://github.com/bep/textandbinaryreader\n        // T', 'A', 'K', '3', '5', 'E', 'M', '1' id uint32, size uint32\n        uint8_t blob_header[16];\n        size_t read_bytes = fread(blob_header, 1, sizeof(blob_header), stream);\n        if (read_bytes != sizeof(blob_header))\n        {\n            fprintf(stderr, \"Error reading blob header\\n\");\n            goto cleanup;\n        }\n        uint32_t blob_id = *(uint32_t *)&blob_header[8];\n        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        }","sourceCodeStart":553,"sourceCodeEnd":589,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/internal/warpc/genwebp/webp.c#L553-L589","documentation":"Printed by genwebp's handle_commands (webp.c:571) when fread cannot read the full 16-byte blob header following a JSON command line. Same semantics as the genavif counterpart (822): EOF, closed stdin, or stream desync. The worker jumps to cleanup without writing a response.","triggerScenarios":"Host closed the pipe after the JSON line but before the blob, host was killed mid-send, the stream is misaligned from a previous bad read so pixel bytes are interpreted as the next header, or a truncated test fixture.","commonSituations":"Context cancellation killing the host between the JSON line and the blob, broken pipe masked by the Go writer, a prior 'Error reading blob data' leaving the stream pointer wrong, or fixtures that send a JSON line with no blob.","solutions":["Treat as fatal worker state on the Go side: respawn genwebp rather than reuse the desynced pipe.","Write JSON + 16-byte header + blob as one logical unit; skip the command entirely if any part cannot be produced.","Audit the prior command's declared blob_size; a wrong value desyncs subsequent reads.","Add a read timeout in the Go warpc client to detect a stuck worker.","In tests, close stdin only after the full blob is written."],"exampleFix":"// before (Go host): wrote JSON then errored before blob\nw.Write(jsonLine)\npix, err := readPixels(ctx)\nif err != nil { return err } // blob never sent\nw.Write(blob)\n\n// after: build the full frame first, write atomically or not at all\nif err != nil { return err }\nframe := append(append(jsonLine, hdr...), pix...)\nw.Write(frame)","handlingStrategy":"retry","validationCode":"func writeWebpRequest(w io.Writer, cmd []byte, blobID uint32, blob []byte) error {\n    if !bytes.HasSuffix(cmd, []byte{'\\n'}) { cmd = append(cmd, '\\n') }\n    hdr := make([]byte, 16)\n    copy(hdr[0:8], []byte(\"TAK35EM1\"))\n    binary.LittleEndian.PutUint32(hdr[8:12], blobID)\n    binary.LittleEndian.PutUint32(hdr[12:16], uint32(len(blob)))\n    frame := append(append(cmd, hdr...), blob...)\n    n, err := w.Write(frame)\n    if err != nil || n != len(frame) { return errShortWrite }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write the full request (JSON + 16-byte header + blob) in one Write call.","Set a context/deadline on the worker pipe to detect a stuck host.","Treat a short header read as fatal and respawn the worker.","Close stdin only after the entire blob has been written."],"tags":["rpc","io","protocol","webp","stdin","stream-desync"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}