{"record":{"id":"e44e2312f9d838de","repo":"microsoft/typescript-go","slug":"api-unexpected-message-while-waiting-for-q-respo","errorCode":null,"errorMessage":"api: unexpected message while waiting for %q response","messagePattern":"api: unexpected message while waiting for %q response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/api/conn_sync.go","lineNumber":200,"sourceCode":"\tif ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\t// Read the response inline.\n\tmsg, err := c.protocol.ReadMessage()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif msg.IsResponse() && msg.ID != nil && msg.ID.String() == method {\n\t\tif msg.Error != nil {\n\t\t\treturn nil, fmt.Errorf(\"api: remote error [%d]: %s\", msg.Error.Code, msg.Error.Message)\n\t\t}\n\t\treturn msg.Result, nil\n\t}\n\n\t// Unexpected message while waiting for response\n\treturn nil, fmt.Errorf(\"api: unexpected message while waiting for %q response\", method)\n}\n\n// Notify sends a notification to the client (no response expected).\nfunc (c *SyncConn) Notify(ctx context.Context, method string, params any) error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\treturn c.protocol.WriteNotification(method, params)\n}\n","sourceCodeStart":182,"sourceCodeEnd":209,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/api/conn_sync.go#L182-L209","documentation":"SyncConn.Call read one message while waiting for the response to `method`, but it was not a response whose pseudo-ID matches. Because the msgpack protocol has no real request IDs, the method name doubles as the ID, so the reply must carry the identical method string and use a response message type. The awaited response is lost and the stream is desynchronized, so the call fails immediately.","triggerScenarios":"The client emits a request or notification before answering the server's Call; the client answers with a different method string (typo, case mismatch); the client replies with MessageTypeResponse instead of MessageTypeCallResponse/CallError so IsResponse() or the ID check fails.","commonSituations":"Client implementations that pipeline messages instead of strict turn-taking; method renames across versions; both sides calling simultaneously over one transport.","solutions":["Make the client answer every server Call before writing anything else - the sync protocol requires strict turn-taking","Verify the reply's method string matches the requested method byte-for-byte (case-sensitive)","Ensure the peer writes MessageTypeCallResponse/MessageTypeCallError tuples when answering a Call","Move long-running client work off the answering path so replies are never delayed past other writes"],"exampleFix":"// before (client loop): queues outgoing messages, then answers server calls\n\n// after (client loop): answer server calls immediately, defer own messages\nif msg.IsRequest() { reply(msg); continue } // nothing else written first","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := conn.Call(ctx, method, params)\nif err != nil && strings.Contains(err.Error(), \"unexpected message while waiting\") {\n\t// stream is desynchronized; restart the connection, do not reuse it\n\treturn restartConn()\n}","preventionTips":["Answer server Calls before writing any other message - strict turn-taking","Echo the method string exactly as received when replying","Reply with call-response/call-error tuples, never request/notification tuples"],"tags":["go","jsonrpc","protocol","desync","request-response","turn-taking"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}