For the past few days, the non-routed agents in Mission Control were aborting almost immediately after being called. Not erroring. Not timing out. Just stopping, silently, within a second or two of starting. The routed agents were fine. Just the non-routed ones.
I went through the obvious suspects first. Race condition in the initialization. A default timeout I’d forgotten to tune. Some config mismatch between how routed and non-routed requests were handled. Nothing.
The stream handling code looked correct. I’d wired up an abort controller to fire when the client disconnected, which is standard practice for HTTP streaming. You don’t want to keep running compute on a connection that’s already gone. The handler was req.on('close', () => { controller.abort() }). Client closes, we abort.
Except req doesn’t close when the client disconnects.
In Node.js, req is the incoming message object. It represents the stuff the client sends to you. Headers, body, request line. Once the client finishes sending that, req closes. Which happens almost immediately, right after the request arrives. Long before any work gets done.
res is the response. Your server’s connection back to the client. That one stays open for the whole duration of the exchange. It closes when the client actually leaves.
So my abort handler was firing the instant the client finished sending their request, which is to say, the instant they showed up. The agents would start spinning up, req would close because the headers were done, the abort controller would trip, and everything would stop. No error. No stack trace. Just done.
The fix was changing req to res. One word.
And here’s the thing that got me: the logic was right. Detect client disconnect, abort the inflight work. That’s exactly what you want in a streaming system. The event was right. The handler was right. The abort was right. I had the wrong object.
That’s the specific kind of bug where nothing tells you it’s wrong. The code compiles. The handler fires. The abort happens. It’s doing exactly what the code says. The code just says the wrong thing. When the symptom is “works then stops” rather than a crash with a trace, you spend a while looking everywhere except the variable name.
I’ve been running a truth pass on Mission Control this week, going back through things I built fast to see if they actually do what I assumed. The CFO billing profile? Never provisioned. Echo, the agent I built for comms? Jarvis had absorbed that role weeks ago and Echo was just sitting there unused. The abort handler? Watching the wrong side of the connection.
Systems do what you tell them to. The problem is that “what you told them” and “what you meant” are sometimes very different things, and there’s no one around to notice.