{"record":{"id":"be0fa4a843b22227","repo":"microsoft/typescript-go","slug":"connection-not-established","errorCode":null,"errorMessage":"Connection not established","messagePattern":"Connection not established","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"_packages/native-preview/src/api/async/client.ts","lineNumber":170,"sourceCode":"                    const result = callback(arg as any);\r\n                    if (name === \"readFile\") {\r\n                        // readFile has 3 returns: string (content), null (not found), undefined (fall back).\r\n                        // JSON-RPC can't distinguish null from undefined, so wrap in object.\r\n                        if (result === undefined) return null;\r\n                        return { content: result };\r\n                    }\r\n                    return result ?? null;\r\n                });\r\n            }\r\n        }\r\n    }\r\n\r\n    async apiRequest<T>(method: string, params?: unknown): Promise<T> {\r\n        if (!this.connected) {\r\n            await this.connect();\r\n        }\r\n        if (!this.connection) {\r\n            throw new Error(\"Connection not established\");\r\n        }\r\n\r\n        const requestType = new RequestType<unknown, T, void>(method);\r\n        if (!this.timing) {\r\n            return this.connection.sendRequest(requestType, params);\r\n        }\r\n\r\n        // Round-trip latency is measured here; byte counts approximate the wire\r\n        // payload via the serialized JSON. Server-side processing time is not\r\n        // carried on the response; it is retrieved separately (via a\r\n        // getServerTiming request) and folded in by getTimingInfo().\r\n        const bytesSent = params === undefined ? 0 : Buffer.byteLength(JSON.stringify(params), \"utf-8\");\r\n        const start = performance.now();\r\n        const result = await this.connection.sendRequest(requestType, params);\r\n        const roundTripMs = performance.now() - start;\r\n        this.timing.record({\r\n            method,\r\n            roundTripMs,\r","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/_packages/native-preview/src/api/async/client.ts#L152-L188","documentation":"Thrown by Client.apiRequest in the async API when, after the lazy connect() step, this.connection is still undefined. connect() sets connected and connection together on success, so in practice this throw indicates a race where close() ran concurrently (close() disposes the connection and clears both flags) or the spawn/socket setup resolved without ever creating the JSON-RPC connection.","triggerScenarios":"Calling close() (or API.close) while requests are still in flight: apiRequest passes the connected check, close() nulls this.connection, then apiRequest hits the second guard; a spawn whose 'spawn' event fired but connection creation failed; issuing requests immediately after close on the same Client instance.","commonSituations":"Fire-and-forget diagnostics not awaited before closing the API in scripts/tests; concurrent dispose and query in editor integrations; long-running watchers that keep requesting while another code path shuts the client down.","solutions":["Await all in-flight requests before calling close(); sequence shutdown after quiescence","Treat this error as 'client was closed': create a fresh API/Client and reconnect rather than reusing the instance","Serialize API usage with a simple mutex/queue if requests and close() can race in your app","Check client state before issuing requests in long-lived loops and stop when shut down"],"exampleFix":"// before\nvoid api.parseConfigFile(file).catch(() => {});\nawait api.close(); // races the in-flight request -> 'Connection not established'\n\n// after\nawait api.parseConfigFile(file); // drain work first\nawait api.close();","handlingStrategy":"retry","validationCode":"// Ensure the client is connected (and not concurrently closed) before requesting\nif (!client.isConnected && !isShuttingDown) {\n    await client.connect();\n}\n// Sequence shutdown elsewhere via a mutex so close() never overlaps requests","typeGuard":null,"tryCatchPattern":"try {\n    result = await client.apiRequest(method, params);\n} catch (e) {\n    if (e instanceof Error && e.message === 'Connection not established') {\n        if (shuttingDown) return; // expected during teardown - skip\n        await client.connect();\n        result = await client.apiRequest(method, params); // retry once after reconnect\n    } else throw e;\n}","preventionTips":["Await all in-flight requests before calling close()/API.close()","Serialize requests vs. shutdown with a mutex or lifecycle flag","Recreate the Client/API instance after close instead of resurrecting it"],"tags":["api","concurrency","connection","json-rpc","native-preview"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}