{"record":{"id":"f45250d53d8bb242","repo":"can1357/oh-my-pi","slug":"dap-adapter-this-adapter-name-is-not-running","errorCode":null,"errorMessage":"DAP adapter ${this.adapter.name} is not running","messagePattern":"DAP adapter (.+?) is not running","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/dap/client.ts","lineNumber":444,"sourceCode":"\t\t}\n\t\ttimeout = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\treject(new Error(`DAP event ${event} timed out after ${timeoutMs}ms`));\n\t\t}, timeoutMs);\n\t\treturn promise;\n\t}\n\n\tasync sendRequest<TBody = unknown>(\n\t\tcommand: string,\n\t\targs?: unknown,\n\t\tsignal?: AbortSignal,\n\t\ttimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,\n\t): Promise<TBody> {\n\t\tif (signal?.aborted) {\n\t\t\tthrow signal.reason instanceof Error ? signal.reason : new ToolAbortError();\n\t\t}\n\t\tif (this.#disposed) {\n\t\t\tthrow new Error(`DAP adapter ${this.adapter.name} is not running`);\n\t\t}\n\t\tconst requestSeq = ++this.#requestSeq;\n\t\tconst request: DapRequestMessage = {\n\t\t\tseq: requestSeq,\n\t\t\ttype: \"request\",\n\t\t\tcommand,\n\t\t\targuments: args,\n\t\t};\n\t\tconst { promise, resolve, reject } = Promise.withResolvers<TBody>();\n\t\t// Suppress \"unhandled rejection\" if the request timer or abort fires\n\t\t// before the caller's `await` subscribes — e.g. while #writeMessage is\n\t\t// still racing a wedged stdin flush. The caller's own `await` still\n\t\t// receives the rejection normally; this handler is a passive guard.\n\t\tpromise.catch(() => {});\n\n\t\tlet timeout: NodeJS.Timeout | undefined;\n\t\tconst cleanup = () => {\n\t\t\tif (timeout) clearTimeout(timeout);","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/dap/client.ts#L426-L462","documentation":"DapClient.sendRequest refuses to send a DAP request over a transport that has already been disposed. Once #disposed is set (after disconnect/termination of the client), any further request — initialize, scopes, continue, etc. — cannot reach the adapter and the client throws immediately instead of hanging until the request timeout. It guards callers against writing into a dead pipe.","triggerScenarios":"Calling any request method on a DapClient after disconnect() or after the session was terminated/disposed; reusing a stale DapSession whose client was disposed when the adapter exited; a race where an abort check passes but disposal happens between it and the disposed check.","commonSituations":"A debug adapter crashed or was terminated and the tool then issues follow-up commands (scopes, variables, stack_trace); a queued async task continues after the session manager disposed the session on adapter exit; a UI keeps issuing evaluate/continue calls after the user stopped debugging.","solutions":["Re-obtain a live session: re-launch or re-attach the debug session before issuing further requests","Check client.isAlive() (or catch this error) before reusing a stored DapSession/DapClient reference","Make sure no concurrent code path calls disconnect/terminate while requests are still in flight; serialize session teardown with pending requests","If you keep a reference across awaits, re-fetch the active session from DapSessionManager instead of caching the client"],"exampleFix":"// before\nconst scopes = await cachedClient.sendRequest('scopes', { frameId });\n// after\nif (!cachedClient.isAlive()) {\n  session = await manager.launch(config); // recreate the session\n}\nconst scopes = await session.client.sendRequest('scopes', { frameId });","handlingStrategy":"try-catch","validationCode":"if (!client.isAlive()) {\n  client = await DapClient.connect({ adapter, cwd });\n}","typeGuard":"function isUsableClient(c: DapClient | null | undefined): c is DapClient {\n  return !!c && c.isAlive();\n}","tryCatchPattern":"try {\n  return await client.sendRequest(command, args, { signal });\n} catch (err) {\n  if (String((err as Error).message).includes('is not running')) {\n    client = await DapClient.connect({ adapter, cwd }); // recreate\n    return await client.sendRequest(command, args, { signal });\n  }\n  throw err;\n}","preventionTips":["Never cache DapClient references across session lifecycle; fetch the active session from DapSessionManager","Check isAlive() before issuing requests after any await that could allow disposal","Serialize session teardown (disconnect/terminate) with in-flight requests via a mutex or queue","Subscribe to adapter exit/close events to invalidate cached clients immediately"],"tags":["dap","lifecycle","disposed-client","debug-adapter"],"backgroundTag":"adapter-not-running","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}