{"record":{"id":"4124bcd5c26aa7bc","repo":"github/copilot-sdk","slug":"failed-to-disconnect-session-this-sessionid","errorCode":null,"errorMessage":"Failed to disconnect session ${this.sessionId}: ${response.error || \"Unknown error\"}","messagePattern":"Failed to disconnect session (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nodejs/src/session.ts","lineNumber":2081,"sourceCode":"     * ```typescript\n     * // Clean up when done — session can still be resumed later\n     * await session.disconnect();\n     * ```\n     */\n    async disconnect(): Promise<void> {\n        if (this.disconnected || this.disconnecting) {\n            return;\n        }\n        this.disconnecting = true;\n        try {\n            let response: { success: boolean; error?: string } = { success: false };\n            for (let attempt = 0; attempt < 2 && !response.success; attempt++) {\n                response = (await this.connection.sendRequest(\"session.detach\", {\n                    sessionId: this.sessionId,\n                })) as { success: boolean; error?: string };\n            }\n            if (!response.success) {\n                throw new Error(\n                    `Failed to disconnect session ${this.sessionId}: ${response.error || \"Unknown error\"}`\n                );\n            }\n            this._markDisconnected();\n        } catch (error) {\n            this.disconnecting = false;\n            throw error;\n        }\n    }\n\n    /** Enables `await using session = ...` syntax for automatic cleanup. */\n    async [Symbol.asyncDispose](): Promise<void> {\n        return this.disconnect();\n    }\n\n    /**\n     * Aborts the currently processing message in this session.\n     *","sourceCodeStart":2063,"sourceCodeEnd":2099,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/session.ts#L2063-L2099","documentation":"Thrown during disconnect() when the `session.detach` request (retried up to twice) still reports success:false. The message includes the session id and the error string returned by the host, or \"Unknown error\" when the response carries no error detail. The session is not marked disconnected in this path.","triggerScenarios":"Host returns { success: false, error } from session.detach on both attempts; connection-level failure so detach never succeeds; session already torn down server-side with an error status.","commonSituations":"Flaky or dropped transport mid-disconnect; server busy/restarting during shutdown; calling disconnect concurrently from multiple code paths so the second call detaches an already-detached session and errors.","solutions":["Inspect response.error in the message to see the host-side detach failure and fix that root cause","Retry disconnect() after a short delay — the SDK only retries twice","Check that the connection is still alive before disconnecting; reconnect if the transport dropped","Treat an already-disconnected session as success and skip detach"],"exampleFix":"// before\nawait session.disconnect(); // throws if host reports failure twice\n// after\ntry {\n  await session.disconnect();\n} catch (err) {\n  if (!session.isDisconnected) {\n    await new Promise((r) => setTimeout(r, 1000));\n    await session.disconnect();\n  }\n}","handlingStrategy":"retry","validationCode":"if (!session.isConnected || session.isDisconnected) return; // nothing to detach\nif (session.disconnecting) return; // avoid concurrent disconnects","typeGuard":null,"tryCatchPattern":"try {\n  await session.disconnect();\n} catch (err) {\n  if (String(err?.message).startsWith(\"Failed to disconnect session\")) {\n    console.warn(`Detach failed: ${err.message}; forcing local cleanup`);\n    session.forceCleanup?.();\n  } else throw err;\n}","preventionTips":["Use a disconnect mutex/flag to prevent concurrent disconnect calls","Check connection health before detaching and reconnect if the transport dropped","Log response.error from the throw message to fix host-side detach failures"],"tags":["disconnect","detach","retry"],"backgroundTag":"http-error-response","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}