microsoft/typescript-go · error

api: unexpected message while waiting for %q response

Error message

api: unexpected message while waiting for %q response

What it means

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.

Source

Thrown at internal/api/conn_sync.go:200

	if ctx.Err() != nil {
		return nil, ctx.Err()
	}

	// Read the response inline.
	msg, err := c.protocol.ReadMessage()
	if err != nil {
		return nil, err
	}

	if msg.IsResponse() && msg.ID != nil && msg.ID.String() == method {
		if msg.Error != nil {
			return nil, fmt.Errorf("api: remote error [%d]: %s", msg.Error.Code, msg.Error.Message)
		}
		return msg.Result, nil
	}

	// Unexpected message while waiting for response
	return nil, fmt.Errorf("api: unexpected message while waiting for %q response", method)
}

// Notify sends a notification to the client (no response expected).
func (c *SyncConn) Notify(ctx context.Context, method string, params any) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.protocol.WriteNotification(method, params)
}

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Make the client answer every server Call before writing anything else - the sync protocol requires strict turn-taking
  2. Verify the reply's method string matches the requested method byte-for-byte (case-sensitive)
  3. Ensure the peer writes MessageTypeCallResponse/MessageTypeCallError tuples when answering a Call
  4. Move long-running client work off the answering path so replies are never delayed past other writes

Example fix

// before (client loop): queues outgoing messages, then answers server calls

// after (client loop): answer server calls immediately, defer own messages
if msg.IsRequest() { reply(msg); continue } // nothing else written first
Defensive patterns

Strategy: try-catch

Try / catch

res, err := conn.Call(ctx, method, params)
if err != nil && strings.Contains(err.Error(), "unexpected message while waiting") {
	// stream is desynchronized; restart the connection, do not reuse it
	return restartConn()
}

Prevention

When it happens

Trigger: 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.

Common situations: Client implementations that pipeline messages instead of strict turn-taking; method renames across versions; both sides calling simultaneously over one transport.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/e44e2312f9d838de. Report an issue: GitHub.