{"record":{"id":"6a3bff8d6f41993c","repo":"can1357/oh-my-pi","slug":"invalid-chunk-size-sizeline","errorCode":null,"errorMessage":"Invalid chunk size: ${sizeLine}","messagePattern":"Invalid chunk size: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/claude-trace-cli.ts","lineNumber":243,"sourceCode":"}\n\nfunction responseHasNoBody(statusCode: number | undefined): boolean {\n\tif (statusCode === undefined) return false;\n\treturn (statusCode >= 100 && statusCode < 200) || statusCode === 204 || statusCode === 304;\n}\n\nfunction parseChunkedBody(buffer: Buffer): ChunkedParseResult {\n\tlet offset = 0;\n\tconst chunks: Buffer[] = [];\n\twhile (true) {\n\t\tconst lineEnd = buffer.indexOf(CRLF, offset);\n\t\tif (lineEnd < 0) return { complete: false, consumed: 0, body: Buffer.alloc(0) };\n\t\tconst sizeLine = buffer.subarray(offset, lineEnd).toString(\"latin1\");\n\t\tconst semicolon = sizeLine.indexOf(\";\");\n\t\tconst sizeText = (semicolon >= 0 ? sizeLine.slice(0, semicolon) : sizeLine).trim();\n\t\tconst size = Number.parseInt(sizeText, 16);\n\t\tif (!Number.isSafeInteger(size) || size < 0) {\n\t\t\tthrow new Error(`Invalid chunk size: ${sizeLine}`);\n\t\t}\n\t\tconst dataStart = lineEnd + CRLF.length;\n\t\tif (size === 0) {\n\t\t\tif (buffer.length < dataStart + CRLF.length) return { complete: false, consumed: 0, body: Buffer.alloc(0) };\n\t\t\tif (buffer.subarray(dataStart, dataStart + CRLF.length).equals(CRLF)) {\n\t\t\t\treturn { complete: true, consumed: dataStart + CRLF.length, body: Buffer.concat(chunks) };\n\t\t\t}\n\t\t\tconst trailerEnd = buffer.indexOf(DOUBLE_CRLF, dataStart);\n\t\t\tif (trailerEnd < 0) return { complete: false, consumed: 0, body: Buffer.alloc(0) };\n\t\t\treturn { complete: true, consumed: trailerEnd + DOUBLE_CRLF.length, body: Buffer.concat(chunks) };\n\t\t}\n\t\tconst chunkEnd = dataStart + size;\n\t\tif (buffer.length < chunkEnd + CRLF.length) return { complete: false, consumed: 0, body: Buffer.alloc(0) };\n\t\tchunks.push(buffer.subarray(dataStart, chunkEnd));\n\t\toffset = chunkEnd + CRLF.length;\n\t}\n}\n","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/claude-trace-cli.ts#L225-L261","documentation":"parseChunkedBody decodes HTTP chunked-transfer-encoding bodies streamed through the local MITM proxy. Before reading each chunk it parses the hex size line; if the line is not a valid non-negative safe hex integer (e.g. because stream framing desynced or the server sent a malformed/truncated size line), it throws this error instead of producing garbage body bytes.","triggerScenarios":"The buffered bytes between CRLF markers are not parseable as a hex chunk size — e.g. the proxy captured mid-stream (joined an existing connection past a chunk boundary), the upstream server sent a malformed chunked response, or binary/deflated bytes shifted the framing so a size line contains non-hex characters.","commonSituations":"Tracing a Claude Code session where the capture attaches after the response already started; an upstream proxy (corporate proxy, VPN) rewriting chunked responses; a server sending non-chunked data mislabeled with Transfer-Encoding: chunked.","solutions":["Restart the trace so the proxy captures the connection from the beginning (full chunk sequence from the first byte).","Check the upstream for intermediaries (corporate proxies, antivirus TLS inspection) that may mangle chunked framing and bypass them.","If the endpoint truly doesn't use chunked encoding, ensure the capture path doesn't assume Transfer-Encoding: chunked for that response.","Log the offending sizeLine (it's embedded in the message) to identify desync and fix the stream-offset bookkeeping."],"exampleFix":"// before: attaching capture mid-stream and feeding arbitrary bytes\nparseChunkedBody(buffer.slice(randomOffset), chunks);\n// after: only feed bytes from a tracked stream offset\nlet offset = 0; // advance only by `consumed` returned from parseChunkedBody\nparseChunkedBody(buffer.subarray(offset), chunks); offset += consumed;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const parsed = parseChunkedBody(buffer.subarray(offset), chunks);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid chunk size\")) {\n    // log err.message (contains raw size line), reset stream offset, re-sync or abort capture\n  } else throw err;\n}","preventionTips":["Feed bytes to the parser only from a monotonically tracked offset advanced by `consumed`","Capture the connection from its first byte — never attach mid-stream","Keep a raw fallback dump of the response so a desync can be diagnosed offline"],"tags":["http","chunked-encoding","parsing","proxy"],"backgroundTag":"invalid-chunk-size","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}