can1357/oh-my-pi · error · Error
DAP adapter ${this.adapter.name} exited before write complet
Error message
DAP adapter ${this.adapter.name} exited before write completed What it means
After writing a DAP message frame to the write sink, DapClient checks whether the adapter process has exited before treating the flush as safe. If the process died, the flush cannot be trusted (the message may never have been consumed) and the client throws instead of silently resolving. This converts a would-be silent message loss into an explicit error.
Source
Thrown at packages/coding-agent/src/dap/client.ts:532
};
await this.#writeMessage(response);
}
/**
* Framed write to the adapter, bounded by {@link WRITE_MESSAGE_TIMEOUT_MS}
* and by adapter exit. Without this bound a wedged adapter stdin used to
* hang the whole client forever. On timeout or exit-before-flush the client
* disposes itself and rethrows.
*/
async #writeMessage(message: DapRequestMessage | DapResponseMessage): Promise<void> {
const content = JSON.stringify(message);
this.#writeSink.write(`Content-Length: ${Buffer.byteLength(content, "utf-8")}\r\n\r\n`);
this.#writeSink.write(content);
const flushResult = this.#writeSink.flush();
if (!(flushResult instanceof Promise)) return;
if (this.#adapterExited) {
throw new Error(`DAP adapter ${this.adapter.name} exited before write completed`);
}
const { promise: guardPromise, reject: guardReject, resolve: guardResolve } = Promise.withResolvers<void>();
const timer = setTimeout(
() =>
guardReject(
new Error(`DAP adapter ${this.adapter.name} write timed out after ${WRITE_MESSAGE_TIMEOUT_MS}ms`),
),
WRITE_MESSAGE_TIMEOUT_MS,
);
const rejectOnExit = () => {
guardReject(new Error(`DAP adapter ${this.adapter.name} exited before write completed`));
};
this.#pendingWriteExitRejectors.add(rejectOnExit);
try {
await Promise.race([flushResult, guardPromise]);
} catch (error) {View on GitHub (pinned to 9690622007)
Solutions
- Catch the error and treat the session as dead: dispose the client and re-launch the adapter if more debugging is needed
- Check that the adapter binary exists and starts reliably (version mismatch, missing runtime) to avoid mid-session exits
- Reduce the window by checking isAlive()/adapter exit before issuing late-session requests like disconnect
- Upgrade or reconfigure the adapter if crashes are recurring (e.g. out-of-memory on large debuggees)
Example fix
// before
await session.client.sendRequest('continue', { threadId });
// after
try {
await session.client.sendRequest('continue', { threadId });
} catch (err) {
if (String(err).includes('exited before write completed')) {
session = await manager.launch(config); // adapter died; restart
} else { throw err; }
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!session.client.isAlive()) {
throw new Error('adapter already exited; skipping send');
} Try / catch
try {
await client.sendRequest('disconnect', {});
} catch (err) {
if (String((err as Error).message).includes('exited before write completed')) {
manager.dispose(client); // adapter died; the write was not delivered
} else { throw err; }
} Prevention
- Treat any adapter exit event as terminal: stop issuing requests afterwards
- Check isAlive() before late-session requests (disconnect, continue after debuggee exit)
- Capture adapter stderr to diagnose why it died mid-session
- Avoid racing terminate with additional requests; await termination before follow-ups
When it happens
Trigger: Sending any DAP request/notification whose flush returns a Promise while the adapter process has already exited (#adapterExited set); e.g. a disconnect/terminate request racing the adapter process shutting down; a burst of writes issued right as the adapter crashes.
Common situations: Adapter segfaults or is killed mid-session and the last request's flush collides with process death; sending a continue/evaluate immediately after the debuggee exits; slow-disk or pipe backpressure extending flush past adapter exit.
Related errors
- Adapter process exited before socket was ready
- DAP adapter ${this.adapter.name} is not running
- Socket not ready after ${timeoutMs}ms
- Adapter process exited before TCP port ${host}:${port} was r
- No active stack frame. Run stack_trace first or supply frame
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/29d7eb611dcafc7d.
Report an issue: GitHub.