{"record":{"id":"6022dc60c2766ecf","repo":"microsoft/typescript-go","slug":"api-failed-to-write-server-timing-response-v","errorCode":null,"errorMessage":"api: failed to write server timing response: %v","messagePattern":"api: failed to write server timing response: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/api/conn_sync.go","lineNumber":93,"sourceCode":"\t\t\tc.handleNotification(ctx, msg)\n\t\t} else {\n\t\t\t// Responses are not expected in the main loop - they are read inline by Call().\n\t\t\treturn errors.New(\"api: unexpected response message in sync connection\")\n\t\t}\n\t}\n}\n\n// handleRequest processes an incoming request.\nfunc (c *SyncConn) handleRequest(ctx context.Context, msg *Message) {\n\t// Intercept the meta-requests for collected server timing before dispatching\n\t// to the handler, so they are answered directly and not themselves recorded.\n\tswitch msg.Method {\n\tcase string(MethodGetServerTiming):\n\t\tc.mu.Lock()\n\t\twriteErr := c.protocol.WriteResponse(msg.ID, serverTimingSnapshot(c.timing))\n\t\tc.mu.Unlock()\n\t\tif writeErr != nil {\n\t\t\tpanic(fmt.Sprintf(\"api: failed to write server timing response: %v\", writeErr))\n\t\t}\n\t\treturn\n\tcase string(MethodResetServerTiming):\n\t\tif c.timing != nil {\n\t\t\tc.timing.reset()\n\t\t}\n\t\tc.mu.Lock()\n\t\twriteErr := c.protocol.WriteResponse(msg.ID, nil)\n\t\tc.mu.Unlock()\n\t\tif writeErr != nil {\n\t\t\tpanic(fmt.Sprintf(\"api: failed to write reset server timing response: %v\", writeErr))\n\t\t}\n\t\treturn\n\t}\n\n\tvar result any\n\tvar err error\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/api/conn_sync.go#L75-L111","documentation":"While handling the $/getServerTiming meta-request, SyncConn failed to write the response and panics with the write error. The transport itself is broken (closed pipe/conn), so the server deliberately crashes rather than silently drop the timing reply.","triggerScenarios":"Client closed the connection (or crashed) between sending getServerTiming and the server's response write; I/O error on the underlying transport; shutdown racing an in-flight meta-request.","commonSituations":"Editor/LSP client timing out and closing the connection right as it polls server timings; tests tearing down connections without draining pending requests.","solutions":["Ensure clients wait for outstanding meta-request responses before closing the connection","Gracefully shut down: stop the read loop before closing the transport","If it recurs in production, capture the write error (broken pipe vs reset) to find who closes first","Treat as a shutdown-race symptom: reproduce with connection-close timing under debug"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// client side: keep the connection open until all meta-request responses arrive\nctx, cancel := context.WithTimeout(ctx, 2*time.Second)\n_, err := conn.Call(ctx, \"getServerTiming\", nil)\nif err != nil { /* transport closed mid-reply: retry on a fresh connection */ }","preventionTips":["Wait for responses before closing connections","Drain pending requests during shutdown","Reproduce close/reply races with timing logs before shipping clients"],"tags":["json-rpc","go","shutdown","transport"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}