t8y2/dbx · error
TDengine response writer stopped
Error message
TDengine response writer stopped
What it means
The runtime reads newline-delimited JSON-RPC requests from stdin and forwards responses over an unbounded mpsc channel to a writer task that serializes them to stdout. This error is returned when responses.send() fails because the receiver side (the stdout writer task) is no longer alive — i.e. the writer task has already exited (e.g. it errored writing to stdout or stdout was closed). The runtime then propagates this error out of run(), terminating the agent process loop.
Source
Thrown at agents/drivers/tdengine/src/runtime.rs:97
}
Ok::<(), anyhow::Error>(())
});
let mut lines = BufReader::new(tokio::io::stdin()).lines();
let mut requests = JoinSet::new();
while let Some(line) = lines.next_line().await? {
while requests.try_join_next().is_some() {}
let line = line.trim();
if line.is_empty() {
continue;
}
let parsed = serde_json::from_str::<RpcRequest>(line);
if parsed.as_ref().is_ok_and(|request| request.method == "shutdown") {
let response = match parsed {
Ok(request) => handle_request(runtime.clone(), request).await,
Err(error) => error_response(Value::Null, "request", None, error.into()),
};
responses.send(response).map_err(|_| anyhow!("TDengine response writer stopped"))?;
while requests.join_next().await.is_some() {}
break;
}
let request_permit = if parsed.as_ref().is_ok_and(|request| is_capacity_exempt(&request.method)) {
None
} else {
match runtime.request_slots.clone().try_acquire_owned() {
Ok(permit) => Some(permit),
Err(_) => {
let response = match parsed {
Ok(request) => error_response(
if request.id.is_null() { json!(1) } else { request.id },
&request.method,
session_id(&request.params),
anyhow!("agent request capacity is temporarily exhausted"),
),
Err(error) => error_response(Value::Null, "request", None, error.into()),
};View on GitHub (pinned to c0390bff16)
Solutions
- Check that the host process keeps the agent's stdout pipe open until after it reads the shutdown response
- Look for an earlier writer-task failure (e.g. broken pipe/EPIPE writing '{"ready":true}' or a response) in logs; the writer error is surfaced later via 'TDengine response writer task failed'
- Ensure the agent process is not closed before the client consumes responses; drain responses before exiting the parent
- If the parent intentionally exits, treat this error as expected teardown noise rather than a bug
Example fix
// before (host closes agent stdout immediately after sending shutdown)
child.stdin.write_all(b"{\"method\":\"shutdown\"}\n");
drop(child.stdout);
// after (drain stdout until process exits)
child.stdin.write_all(b"{\"method\":\"shutdown\"}\n");
let output = child.wait_with_output()?; // keep stdout open and read it Defensive patterns
Strategy: try-catch
Validate before calling
const child = spawn(agentCmd, { stdio: ["pipe", "pipe", "pipe"] });
if (!child.stdout || child.stdout.destroyed) throw new Error("agent stdout pipe unavailable"); Type guard
function hasLiveStdout(child) {
return child?.stdout != null && !child.stdout.destroyed && child.exitCode === null;
} Try / catch
try {
await agent.run();
} catch (e) {
if ("TDengine response writer stopped".length && /response writer (stopped|task failed)/.test(String(e.cause ?? e))) {
// stdout pipe closed: restart the agent and re-handshake
await restartAgent();
} else { throw e; }
} Prevention
- Always keep a reader attached to agent stdout for the agent's entire lifetime; never drop/close it early
- Read the shutdown response before terminating or closing the pipe to the agent
- Monitor the child's stderr and exit events so a dead writer is detected immediately
- Treat any writer-stopped error as fatal to the agent loop: restart the process rather than reusing it
When it happens
Trigger: The writer task spawned at runtime.rs:70 panicked or returned Err (for example stdout write/flush failed because the parent process closed the pipe), the channel's receiver was dropped, or the agent's stdout was redirected to a consumer that exited while a 'shutdown' response was still being sent.
Common situations: Parent/host process killed or closed the agent's stdout pipe while the agent was still finishing work; EPIPE when stdout is a pipe whose reader died; a host bug that spawned the agent without a live stdout consumer; the agent outliving its host during teardown.
Related errors
- DBX MCP process is not available
- Custom H2 JDBC JAR does not exist: " + path
- H2 MVStore file is too small to contain valid headers: " + f
- Failed to read CLOB value
- agent exited before {method}
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/3c4bbdf828e9bc1b.
Report an issue: GitHub.