[Bug Report]: Streaming startup failure leaves exclusive Agent Server busy
Version
Current main at c2ad42e3eb9b27830db41a3e6f51ca7179d9b168 (@tarko/agent-server / @tarko/agent-server-next).
Issue Type
- Agent TARS Server (
@tarko/agent-server)
Model Provider
Provider-independent. Any agent whose streaming run() rejects before returning an AsyncIterable can trigger it.
Problem Description
In exclusive mode, a streaming query that fails while starting leaves the server's running-session slot occupied. Subsequent requests are then rejected as busy even though no stream is running.
The same control flow exists in both server implementations:
multimodal/tarko/agent-server/src/core/AgentSession.ts,runQueryStreaming()multimodal/tarko/agent-server-next/src/services/session/AgentSession.ts,runQueryStreaming()
The sequence is:
setRunningSession(this.id)marks the exclusive slot busy.await this.agent.run(runOptions)rejects before returning a stream.- The catch block returns
createErrorEventStream(handledError). clearRunningSession()only exists inwrapStreamForExclusiveMode()for a successfully returned stream, so the synthetic error stream never releases the slot.canAcceptNewRequest()continues to return false until unrelated cleanup or abort logic happens.
Minimal provider-free reproduction with a fake agent/server:
const server = createExclusiveServerStub();
const session = createSession(server, {
run: async () => {
throw new Error('startup failed');
},
});
const errorStream = await session.runQueryStreaming({ input: 'test' });
for await (const _event of errorStream) {}
expect(server.getRunningSessionId()).toBeNull(); // actual: session id remains set
expect(server.canAcceptNewRequest()).toBe(true); // actual: falseExpected: after the startup failure has been converted to its error stream, the exclusive slot is released according to a documented lifecycle rule.
Actual: the failed session remains the running session and blocks unrelated later requests.
Impact: a transient model/configuration/agent initialization error can wedge an exclusive Agent Server until manual cleanup or restart. Both the current and agent-server-next implementations contain the same path.
This is issue-only for now because a complete fix needs the maintainers to choose whether the exclusive slot is released immediately when agent.run() rejects or only after the caller consumes/closes the synthetic error stream, and the behavior must remain consistent across both server generations and their HTTP/SSE lifecycle. Regression coverage should exercise rejection before stream creation, partial/abandoned consumption of the error stream, and a subsequent request in exclusive mode.
Error Logs
Representative observable state after a rejected streaming start:
runningSessionId: <failed session id>
canAcceptNewRequest(): false
active stream: noneSource: bytedance/UI-TARS-desktop