github/copilot-sdk · error

failed to unmarshal send response

Error message

failed to unmarshal send response: %w

What it means

After a successful session.send request, Session.Send unmarshals the JSON result into sessionSendResponse to extract the message ID. This error indicates the server returned a response body that is not the expected JSON shape, so no message ID could be parsed.

Solutions

  1. Log the raw `result` payload to see what the server actually returned.
  2. Check for a client/server version mismatch and align versions.
  3. Verify no proxy or gateway is intercepting and rewriting the RPC response.
  4. Retry the send; if reproducible, report the malformed response to the server maintainers.

Example fix

// before
// server returns {"id":"..."} but client expects message_id — upgrade client
// after
// go get -u github.com/<org>/go@v<matching-server-version>
Defensive patterns

Strategy: try-catch

Try / catch

id, err := session.Send(ctx, msg, opts)
if err != nil {
    if strings.Contains(err.Error(), "failed to unmarshal send response") {
        // log raw payload, check client/server version compatibility
    }
}

Prevention

When it happens

Trigger: json.Unmarshal(result, &response) fails in Send — the RPC result is malformed JSON or lacks the fields sessionSendResponse expects (e.g. missing message_id).

Common situations: Client and server version mismatch where the server's send response schema changed; a proxy/gateway returning an error page instead of the RPC result; a server bug emitting an empty or HTML body.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/c3b8f3b71bed4fdc. Report an issue: GitHub.

Appendix: source

Thrown at go/session.go:452

		Prompt:         options.Prompt,
		Source:         options.Source,
		DisplayPrompt:  options.DisplayPrompt,
		Attachments:    options.Attachments,
		Mode:           options.Mode,
		AgentMode:      options.AgentMode,
		Traceparent:    traceparent,
		Tracestate:     tracestate,
		RequestHeaders: options.RequestHeaders,
	}

	result, err := s.client.Request(ctx, "session.send", req)
	if err != nil {
		return "", fmt.Errorf("failed to send message: %w", err)
	}

	var response sessionSendResponse
	if err := json.Unmarshal(result, &response); err != nil {
		return "", fmt.Errorf("failed to unmarshal send response: %w", err)
	}
	return response.MessageID, nil
}

// SendPrompt is a convenience wrapper for [Session.Send] that takes a plain
// prompt string instead of a [MessageOptions] struct. Equivalent to:
//
//	session.Send(ctx, copilot.MessageOptions{Prompt: prompt})
func (s *Session) SendPrompt(ctx context.Context, prompt string) (string, error) {
	return s.Send(ctx, MessageOptions{Prompt: prompt})
}

// SendAndWait sends a message to this session and waits until the session becomes idle.
//
// This is a convenience method that combines [Session.Send] with waiting for
// the session.idle event. Use this when you want to block until the assistant
// has finished processing the message.
//

View on GitHub (pinned to cd8cf15dc3)