Repository navigation
Conversation
Node's documented signatures are response.end([data[, encoding]][, callback]) and request.end([data[, encoding]][, callback]). The record-mode patches on ServerResponse.end and ClientRequest.end only checked `if (chunk)`, so a callback-only end() pushed the function into the body chunk list. Server side, combineChunks() then threw ERR_INVALID_ARG_TYPE inside the nextTick body processing; the error was logged and the span was exported without its response body, even when res.write() had already sent one. Client side, Buffer.from(callback, encoding) threw synchronously out of the user's own req.end(cb) call, so the outbound request never left the process while recording was on. Guard both patches (and the identical ServerResponse.end patch in the Next.js instrumentation) with typeof chunk !== "function" and pass the argument through to the original end() unchanged, which already handles the callback form.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In RECORD mode, calling
req.end(callback)on an instrumented outboundhttp/httpsrequest throwsTypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer ... Received functionfrom the user's own call site, so the request never goes out. On the inbound side, a handler that ends withres.write(body); res.end(callback)still responds normally, but the recorded server span is exported without its response body and an error is logged. Both are the documented Node signatureend([data[, encoding]][, callback]).Root cause
The record-mode patches on
ServerResponse.end(http/Instrumentation.ts) andClientRequest.end(_captureClientRequestBody) only checkif (chunk)before treating the first argument as body data. A callback-onlyend()therefore pushes the function into the chunk list:combineChunks()callsBuffer.from(fn)inside theprocess.nextTickbody processing; it throws, is caught and logged, andoutputValue.bodyis never set.Buffer.from(chunk, encoding)throws synchronously inside the patchedreq.end, propagating to the caller.The client-side response
endlistener already guards withtypeof chunk !== "function", and the replay-sideTdMockClientRequest.end()handles the callback form correctly; the two record-modeend()patches (and the identical one in the Next.js instrumentation) were missed.Fix
src/instrumentation/libraries/http/Instrumentation.ts: in the patchedres.endandreq.end, only capturechunkwhentypeof chunk !== "function". The argument is still passed through to the originalend()unchanged, which already handlesend(cb).src/instrumentation/libraries/nextjs/Instrumentation.ts: same guard on the patchedactualRes.end.Tests
src/instrumentation/libraries/http/integration-tests/end-callback.test.ts(new, follows the existing in-memory-adapter integration pattern; requireshttpafterTuskDrift.initialize()likesmall.test.tsso the RITM hook patches it):res.write(json); res.end(() => {}); asserts the SERVER span'soutputValue.bodydecodes to the written JSON. Fails on main withoutputValue.body === undefined.http.requestis ended withreq.end(() => {}); asserts the upstream call completes (200) and the CLIENT span'sinputValue.bodydecodes to the request body. Fails on main with theERR_INVALID_ARG_TYPE ... Received functionerror surfacing as a 500.src/instrumentation/libraries/http/**/*.test.tspass (171 tests);tsc --noEmitandnpm run buildpass.Not changed
res.write/req.writepatches are untouched:write()has no callback-only form.▚▚ Shipped by breken — self-healing software. This one's on us. breken.ai