microsoft/typescript-go · error
SyncRpcChannel is closed
Error message
SyncRpcChannel is closed
What it means
Every request goes through ensureOpen(), which throws 'SyncRpcChannel is closed' once readFd is negative — the sentinel set by close() (and the cleanup path when the child dies). The channel is single-use: after closing (explicitly or because the child terminated), any further apiRequest/request call fails fast instead of writing to dead file descriptors.
Source
Thrown at _packages/native-preview/src/api/syncChannel.ts:272
}
// Destroy the stdio streams so that their pipe handles are closed
// and no longer prevent the event loop from draining.
this.child.stdout?.destroy();
this.child.stdin?.destroy();
this.child.kill();
this.readFd = -1;
this.writeFd = -1;
}
catch {
// swallow – process may already be dead
}
}
// ── Core request loop ───────────────────────────────────────────
private ensureOpen(): void {
if (this.readFd < 0) {
throw new Error("SyncRpcChannel is closed");
}
}
private getMethodBuf(method: string): Buffer {
let buf = this.methodBufCache.get(method);
if (buf === undefined) {
buf = Buffer.from(method, "utf-8");
this.methodBufCache.set(method, buf);
}
return buf;
}
private requestBytesSync(method: string, payload: Buffer | Uint8Array | string): Buffer {
const methodBuf = this.getMethodBuf(method);
if (this.collectTiming) {
this.lastBytesSent = typeof payload === "string"
? Buffer.byteLength(payload, "utf-8")
: payload.length;
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Track closed state in your own wrapper and skip or re-create requests after close()
- Serialize close() with pending requests (same tick ordering) since the channel is synchronous
- Recreate the Client/API instance if you need to keep working after a deliberate close
Example fix
// before
api.close();
api.getProjectSnapshot("tsconfig.json"); // channel closed -> throws
// after
api.close();
api = new API({ cwd }); // fresh instance
api.getProjectSnapshot("tsconfig.json"); Defensive patterns
Strategy: validation
Validate before calling
class SafeApi { constructor(private api: API) {}
private closed = false;
close() { this.closed = true; this.api.close(); }
getProjectSnapshot(p: string) {
if (this.closed) throw new Error("API already closed; recreate it");
return this.api.getProjectSnapshot(p);
} } Try / catch
try { result = api.someMethod(); } catch (e) { if ((e as Error).message === "SyncRpcChannel is closed") { api = new API({ cwd }); result = api.someMethod(); } else throw e; } Prevention
- Wrap the API and record closed state; block requests after close()
- Serialize close with pending synchronous requests
- Create a new Client/API instance when work must continue after close
When it happens
Trigger: Calling any API method after client.close(); issuing requests after the tsgo child crashed and the channel cleaned up; a request racing close() from another synchronous entry point.
Common situations: Editor shutdown ordering bugs (dispose runs while queued queries still execute); forgetting that API.close() cascades to the channel; long-lived singletons that get closed on error then reused.
Related errors
- Cannot run a temporary file update on an inactive snapshot
- Snapshot is disposed
- ${method} returned null symbol for ${source.constructor.name
- ${method} returned no type for ${source.constructor.name} ${
- ${method} returned null signature for ${source.constructor.n
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/d5a909a80e992175.
Report an issue: GitHub.