{"record":{"id":"29d7eb611dcafc7d","repo":"can1357/oh-my-pi","slug":"dap-adapter-this-adapter-name-exited-before-wri","errorCode":null,"errorMessage":"DAP adapter ${this.adapter.name} exited before write completed","messagePattern":"DAP adapter (.+?) exited before write completed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/dap/client.ts","lineNumber":532,"sourceCode":"\t\t};\n\t\tawait this.#writeMessage(response);\n\t}\n\n\t/**\n\t * Framed write to the adapter, bounded by {@link WRITE_MESSAGE_TIMEOUT_MS}\n\t * and by adapter exit. Without this bound a wedged adapter stdin used to\n\t * hang the whole client forever. On timeout or exit-before-flush the client\n\t * disposes itself and rethrows.\n\t */\n\tasync #writeMessage(message: DapRequestMessage | DapResponseMessage): Promise<void> {\n\t\tconst content = JSON.stringify(message);\n\t\tthis.#writeSink.write(`Content-Length: ${Buffer.byteLength(content, \"utf-8\")}\\r\\n\\r\\n`);\n\t\tthis.#writeSink.write(content);\n\t\tconst flushResult = this.#writeSink.flush();\n\t\tif (!(flushResult instanceof Promise)) return;\n\n\t\tif (this.#adapterExited) {\n\t\t\tthrow new Error(`DAP adapter ${this.adapter.name} exited before write completed`);\n\t\t}\n\n\t\tconst { promise: guardPromise, reject: guardReject, resolve: guardResolve } = Promise.withResolvers<void>();\n\t\tconst timer = setTimeout(\n\t\t\t() =>\n\t\t\t\tguardReject(\n\t\t\t\t\tnew Error(`DAP adapter ${this.adapter.name} write timed out after ${WRITE_MESSAGE_TIMEOUT_MS}ms`),\n\t\t\t\t),\n\t\t\tWRITE_MESSAGE_TIMEOUT_MS,\n\t\t);\n\t\tconst rejectOnExit = () => {\n\t\t\tguardReject(new Error(`DAP adapter ${this.adapter.name} exited before write completed`));\n\t\t};\n\t\tthis.#pendingWriteExitRejectors.add(rejectOnExit);\n\n\t\ttry {\n\t\t\tawait Promise.race([flushResult, guardPromise]);\n\t\t} catch (error) {","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/dap/client.ts#L514-L550","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nawait session.client.sendRequest('continue', { threadId });\n// after\ntry {\n  await session.client.sendRequest('continue', { threadId });\n} catch (err) {\n  if (String(err).includes('exited before write completed')) {\n    session = await manager.launch(config); // adapter died; restart\n  } else { throw err; }\n}","handlingStrategy":"try-catch","validationCode":"if (!session.client.isAlive()) {\n  throw new Error('adapter already exited; skipping send');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await client.sendRequest('disconnect', {});\n} catch (err) {\n  if (String((err as Error).message).includes('exited before write completed')) {\n    manager.dispose(client); // adapter died; the write was not delivered\n  } else { throw err; }\n}","preventionTips":["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"],"tags":["dap","process-exit","io","debug-adapter"],"backgroundTag":"process-exited-mid-write","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}