JS stdio protocol doesn't handle massive metafile (2179827550 bytes) (original) (raw)

After completing a build of a very large project with metafile enabled, the JS wrapper for esbuild fails with:

Error: Invalid packet
    at ByteBuffer._read (node_modules\esbuild\lib\main.js:162:13)
    at ByteBuffer.read32 (node_modules\esbuild\lib\main.js:171:40)
    at decodePacket (node_modules\esbuild\lib\main.js:123:15)
    at handleIncomingPacket (node_modules\esbuild\lib\main.js:652:18)
    at Socket.readFromStdout (node_modules\esbuild\lib\main.js:582:7)
    at Socket.emit (node:events:518:28)
    at addChunk (node:internal/streams/readable:561:12)
    at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)
    at Readable.push (node:internal/streams/readable:392:5)
    at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)

I have tracked this down to the readUInt32LE implementation:

export function readUInt32LE(buffer: Uint8Array, offset: number): number {
return buffer[offset++] |
(buffer[offset++] << 8) |
(buffer[offset++] << 16) |
(buffer[offset++] << 24)
}

Because (most) JS bitwise operators cast the operands down to signed 32 bit integers, this actually behaves like "readInt32LE" and ends up with a negative number when read as a length.

Potential fix:

export function readUInt32LE(buffer: Uint8Array, offset: number): number { return buffer[offset++] + (buffer[offset++] * 28) + (buffer[offset++] * 216) + (buffer[offset++] * 2**24) }

I'm not sure what consequences that would have elsewhere readUInt32LE is used, but it works for me locally.

Apologies I don't have a reproduction to share; it would need to be a project with a lot of inputs and outputs that explodes the size of the metafile.