[REQUIRED] Environment info
firebase-tools: 15.24.0 (also present on master today)
Platform: macOS (platform-independent — pure JS logic bug)
[REQUIRED] Test case
The defect is in src/emulator/types.ts, in EmulatorLog.flush():
EmulatorLog.WAITING_FOR_FLUSH = true;
if (process.send) {
(process.send as any)(nextMsg, undefined, {}, (err: any) => {
if (err) {
process.stderr.write(err); // <-- err is an Error object
}
EmulatorLog.WAITING_FOR_FLUSH = EmulatorLog.LOG_BUFFER.length > 0;
this.flush();
});
}
process.send()'s callback receives an Error object (per the Node docs), but stream.write() accepts only a string, Buffer, TypedArray, or DataView. Passing an Error makes write() throw synchronously.
Minimal reproduction of the underlying write() behavior:
process.stderr.write(new Error('boom'));
// TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string
// or an instance of Buffer, TypedArray, or DataView. Received an instance of Error
[REQUIRED] Steps to reproduce
The code path fires whenever the functions-runtime child process fails to IPC a log message back to the CLI — i.e. when something has already gone wrong with that subprocess (it died, was killed, or the channel closed). We hit it on CI runners under memory/CPU saturation, where the emulator subprocess is killed mid-run.
It is not reproducible on demand without inducing an IPC failure, because it requires process.send()'s callback to be invoked with a non-null err.
[REQUIRED] Expected behavior
The error's message/stack is written to stderr, so the operator can see why the IPC to the functions runtime failed.
[REQUIRED] Actual behavior
process.stderr.write(err) throws TypeError: The "chunk" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Error, inside a callback on the microtask queue with no surrounding try/catch. That makes it an uncaught exception, so:
- The original error is destroyed. Whatever
err actually said — the real reason the emulator subprocess became unreachable — is never printed.
- The replacement error is misleading. Operators see a
TypeError about stream chunk types, which points at Node's stream internals rather than at the emulator failure that actually happened.
Observed stack:
TypeError: The "chunk" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Error
at _write (node:internal/streams/writable:480)
at Writable.write (node:internal/streams/writable:508)
at node_modules/firebase-tools/lib/emulator/types.js:180
process.stderr.write(err);
at process.processTicksAndRejections (node:internal/process/task_queues:89)
The practical cost is diagnostic and it compounds: this fires precisely when an emulator subprocess has failed, which is exactly the moment the error text matters most — and firebase-tools' own reporter is what swallows it. It has made two separate emulator-flake investigations in our repo materially harder, because CI logs contain only the TypeError and never the underlying cause.
Suggested fix
Stringify before writing:
(process.send as any)(nextMsg, undefined, {}, (err: any) => {
if (err) {
process.stderr.write(`${(err && (err.stack || err.message)) || err}\n`);
}
...
});
Happy to open a PR if that shape is agreeable.
For reference, we are carrying this locally as a patch-package patch against 15.24.0 until it is fixed upstream.
[REQUIRED] Environment info
firebase-tools: 15.24.0 (also present on
mastertoday)Platform: macOS (platform-independent — pure JS logic bug)
[REQUIRED] Test case
The defect is in
src/emulator/types.ts, inEmulatorLog.flush():process.send()'s callback receives anErrorobject (per the Node docs), butstream.write()accepts only a string,Buffer,TypedArray, orDataView. Passing anErrormakeswrite()throw synchronously.Minimal reproduction of the underlying
write()behavior:[REQUIRED] Steps to reproduce
The code path fires whenever the functions-runtime child process fails to IPC a log message back to the CLI — i.e. when something has already gone wrong with that subprocess (it died, was killed, or the channel closed). We hit it on CI runners under memory/CPU saturation, where the emulator subprocess is killed mid-run.
It is not reproducible on demand without inducing an IPC failure, because it requires
process.send()'s callback to be invoked with a non-nullerr.[REQUIRED] Expected behavior
The error's message/stack is written to stderr, so the operator can see why the IPC to the functions runtime failed.
[REQUIRED] Actual behavior
process.stderr.write(err)throwsTypeError: The "chunk" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Error, inside a callback on the microtask queue with no surrounding try/catch. That makes it an uncaught exception, so:erractually said — the real reason the emulator subprocess became unreachable — is never printed.TypeErrorabout stream chunk types, which points at Node's stream internals rather than at the emulator failure that actually happened.Observed stack:
The practical cost is diagnostic and it compounds: this fires precisely when an emulator subprocess has failed, which is exactly the moment the error text matters most — and firebase-tools' own reporter is what swallows it. It has made two separate emulator-flake investigations in our repo materially harder, because CI logs contain only the
TypeErrorand never the underlying cause.Suggested fix
Stringify before writing:
Happy to open a PR if that shape is agreeable.
For reference, we are carrying this locally as a
patch-packagepatch against 15.24.0 until it is fixed upstream.