Skip to content

Commit 616878f

Browse files
dylayedclaude
andcommitted
fix: decode only the viewed bytes of Uint8Array log data
When execution-id logging is enabled, stdout/stderr writes are intercepted and re-decoded to attach execution context. The Uint8Array branch decoded `data.buffer`, which is the entire backing ArrayBuffer and ignores the view's `byteOffset`/`byteLength`. Node.js allocates most Buffers from a shared internal pool, so a Buffer written to stdout is frequently a view with a non-zero byteOffset into a much larger ArrayBuffer. Decoding `data.buffer` therefore captured unrelated pool bytes surrounding the real message, producing corrupted log output (e.g. logging the whole 8 KiB pool instead of "hello world"). Decode `Buffer.from(data.buffer, data.byteOffset, data.byteLength)` so only the bytes the view spans are read. Added a regression test that logs through a pooled, non-zero-offset view. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dkx5aLkUwDrmK2uC8dbALu
1 parent 7d08f3b commit 616878f

2 files changed

Lines changed: 21 additions & 1 deletion

File tree

src/logger.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,15 @@ function processData(data: Uint8Array | string, encoding?: BufferEncoding) {
163163
let decodedData;
164164
try {
165165
if (data instanceof Uint8Array) {
166-
decodedData = Buffer.from(data.buffer).toString();
166+
// Decode only the bytes the view actually spans. A Uint8Array (e.g. a
167+
// Buffer carved from Node's shared pool) is often a view into a larger
168+
// ArrayBuffer, so reading `data.buffer` directly would include unrelated
169+
// bytes outside the view's byteOffset/byteLength range.
170+
decodedData = Buffer.from(
171+
data.buffer,
172+
data.byteOffset,
173+
data.byteLength,
174+
).toString();
167175
} else {
168176
decodedData = Buffer.from(data, encoding).toString();
169177
}

test/logger.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,18 @@ describe('getModifiedData', () => {
147147
assert.equal(modifiedData, expectedTextOutput);
148148
});
149149

150+
it('uint8Array that is a view into a larger (pooled) buffer', () => {
151+
// Node.js allocates most Buffers from a shared internal pool, so a Buffer
152+
// written to stdout/stderr is frequently a view with a non-zero byteOffset
153+
// into a much larger ArrayBuffer. The logger must honor the view's
154+
// byteOffset/byteLength rather than decoding the entire backing buffer.
155+
const pool = Buffer.alloc(64, 0x2e /* '.' */);
156+
const view = pool.subarray(8, 8 + sampleText.length);
157+
view.write(sampleText);
158+
const modifiedData = getModifiedData(view);
159+
assert.equal(modifiedData, expectedTextOutput);
160+
});
161+
150162
it('simple text with error', () => {
151163
const modifiedData = getModifiedData(sampleText, undefined, true);
152164
const expectedOutput =

0 commit comments

Comments
 (0)