Before submitting
Area
apps/server
Steps to reproduce
-
Start the nightly headless server:
-
Connect a T3 Code client.
-
Have the client present a stale or otherwise invalid session token. The server repeatedly logs:
Rejected authenticated session credential.
reason: Invalid session token signature.
-
Close or interrupt the client connection while the resulting JSON 401 response is being written.
-
The server emits an unhandled EPIPE and terminates.
This was captured from a real incident. It was not intentionally repeated because reproducing it terminates the server and may interrupt active provider work.
A focused deterministic regression test could:
- Start
NodeHttpServer.layerTest in a child process.
- Expose a route returning a small
schemaJson response.
- Destroy the client socket after sending the request but before the response finishes.
- Assert that the request fails locally while the server process remains alive.
Expected behavior
A disconnected client should terminate only that request. An invalid session credential should return a 401, or fail harmlessly if the client has already disconnected.
The T3 server must remain alive, preserving other clients and in-flight provider work.
Actual behavior
The socket emits an unhandled EPIPE, which terminates the entire Node process. All clients lose access, the server stops listening, and a manual restart is required.
Impact
Blocks work completely
This can disconnect every client and leave in-flight or pending work in a stale state.
Version or commit
- T3 Code
0.0.29-nightly.20260723.887
- Release commit
979854895cc539269213915cd6581cf444089bdc
@effect/platform-node 4.0.0-beta.78
effect 4.0.0-beta.78
The relevant dependency and response path remain unchanged on the inspected main checkout.
Environment
- Linux x86_64
- Node.js
v24.18.0
- T3 Code desktop client
Logs or stack traces
Rejected authenticated session credential.
reason: Invalid session token signature.
node:events:487
throw er; // Unhandled 'error' event
^
Error: write EPIPE
at afterWriteDispatched (node:internal/stream_base_commons:159:15)
at writevGeneric (node:internal/stream_base_commons:142:3)
at Socket._writeGeneric (node:net:1024:11)
at Socket._writev (node:net:1033:8)
at doWrite (node:internal/streams/writable:594:12)
at clearBuffer (node:internal/streams/writable:773:5)
at Writable.uncork (node:internal/streams/writable:529:7)
at ServerResponse.end (node:_http_outgoing:1095:19)
at handleResponse (@effect/platform-node/dist/NodeHttpServer.js:377:24)
at effect/dist/unstable/http/HttpEffect.js:54:32
Emitted 'error' event on Socket instance at:
at emitErrorNT (node:internal/streams/destroy:170:8)
at emitErrorCloseNT (node:internal/streams/destroy:129:3)
at process.processTicksAndRejections (node:internal/process/task_queues:90:21) {
errno: -32,
code: 'EPIPE',
syscall: 'write'
}
Node.js v24.18.0
free(): invalid pointer
Aborted
Root-cause analysis
This appears to be the failure chain:
-
T3 maps an invalid session credential to EnvironmentAuthInvalidError.
-
That error is serialized as a schema-backed JSON 401.
-
Effect encodes schema JSON as a Uint8Array.
-
For Uint8Array bodies smaller than 1 MiB, @effect/platform-node calls:
if (body.body.length < 1024 * 1024) {
nodeResponse.end(body.body);
return Effect.void;
}
This path does not wait for completion or attach an error handler.
-
If the peer closes during the write, Node emits EPIPE on the socket. With no listener, it becomes an unhandled event and terminates the process.
The later free(): invalid pointer occurs during shutdown and may be a secondary native-cleanup failure. The first actionable failure is the unhandled socket EPIPE.
Relevant source:
Suggested fix direction
Handle completion and socket errors for small Uint8Array responses just as carefully as streamed and large responses. An EPIPE or ECONNRESET caused by a client disconnect should become a request-scoped failure or interruption, never an unhandled process event.
This may need an upstream Effect fix, a temporary dependency patch in T3 Code, or both. A regression test should explicitly destroy the client connection during a small response and verify that the server remains alive.
Workaround
There is no reliable preventative workaround because any client can disconnect during a response. Restarting the server restores access, and removing or re-pairing the stale client may reduce this particular trigger, but neither addresses the underlying crash condition.
Before submitting
Area
apps/serverSteps to reproduce
Start the nightly headless server:
Connect a T3 Code client.
Have the client present a stale or otherwise invalid session token. The server repeatedly logs:
Close or interrupt the client connection while the resulting JSON
401response is being written.The server emits an unhandled
EPIPEand terminates.This was captured from a real incident. It was not intentionally repeated because reproducing it terminates the server and may interrupt active provider work.
A focused deterministic regression test could:
NodeHttpServer.layerTestin a child process.schemaJsonresponse.Expected behavior
A disconnected client should terminate only that request. An invalid session credential should return a
401, or fail harmlessly if the client has already disconnected.The T3 server must remain alive, preserving other clients and in-flight provider work.
Actual behavior
The socket emits an unhandled
EPIPE, which terminates the entire Node process. All clients lose access, the server stops listening, and a manual restart is required.Impact
Blocks work completelyThis can disconnect every client and leave in-flight or pending work in a stale state.
Version or commit
0.0.29-nightly.20260723.887979854895cc539269213915cd6581cf444089bdc@effect/platform-node4.0.0-beta.78effect4.0.0-beta.78The relevant dependency and response path remain unchanged on the inspected
maincheckout.Environment
v24.18.0Logs or stack traces
Root-cause analysis
This appears to be the failure chain:
T3 maps an invalid session credential to
EnvironmentAuthInvalidError.That error is serialized as a schema-backed JSON
401.Effect encodes schema JSON as a
Uint8Array.For
Uint8Arraybodies smaller than 1 MiB,@effect/platform-nodecalls:This path does not wait for completion or attach an error handler.
If the peer closes during the write, Node emits
EPIPEon the socket. With no listener, it becomes an unhandled event and terminates the process.The later
free(): invalid pointeroccurs during shutdown and may be a secondary native-cleanup failure. The first actionable failure is the unhandled socketEPIPE.Relevant source:
EnvironmentAuth.tsenvironmentHttp.tsHttpBody.tsNodeHttpServer.tsSuggested fix direction
Handle completion and socket errors for small
Uint8Arrayresponses just as carefully as streamed and large responses. AnEPIPEorECONNRESETcaused by a client disconnect should become a request-scoped failure or interruption, never an unhandled process event.This may need an upstream Effect fix, a temporary dependency patch in T3 Code, or both. A regression test should explicitly destroy the client connection during a small response and verify that the server remains alive.
Workaround
There is no reliable preventative workaround because any client can disconnect during a response. Restarting the server restores access, and removing or re-pairing the stale client may reduce this particular trigger, but neither addresses the underlying crash condition.