github/copilot-sdk · error

failed to send message

Error message

failed to send message: %w

What it means

Session.Send issues a "session.send" request over the client's transport. If that request fails (transport error, non-success response, timeout), the error is wrapped as "failed to send message". The empty string message ID is returned since no response arrived.

Solutions

  1. Inspect the wrapped inner error to distinguish transport failure vs server rejection.
  2. Check that the agent server is running and the session is still connected (session.Disconnect/Reconnect as needed).
  3. Increase the context timeout for large prompts or slow networks.
  4. Retry the send with backoff if the inner error is transient (connection reset, timeout).

Example fix

// before
ctx := context.Background()
id, err := session.Send(ctx, msg) // hangs/fails on slow networks
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
id, err := session.Send(ctx, msg)
Defensive patterns

Strategy: retry

Try / catch

var id string
var err error
for i := 0; i < 3; i++ {
    id, err = session.Send(ctx, msg, opts)
    if err == nil || !isTransient(err) {
        break
    }
    time.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)
}

Prevention

When it happens

Trigger: s.client.Request(ctx, "session/send", req) returns an error during Send (directly or via SendPrompt/SendAndWait) — connection drop, server unavailable, context deadline exceeded, or authentication failure.

Common situations: Server process stopped or crashed; network interruption mid-session; context deadline too short for large prompts; session already closed on the server.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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

Appendix: source

Thrown at go/session.go:447

//	}
func (s *Session) Send(ctx context.Context, options MessageOptions) (string, error) {
	traceparent, tracestate := getTraceContext(ctx)
	req := sessionSendRequest{
		SessionID:      s.SessionID,
		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.

View on GitHub (pinned to cd8cf15dc3)