charmbracelet/crush · error
failed to send message to agent: status code %d: %s
Error message
failed to send message to agent: status code %d: %s
What it means
SendMessage accepts 200 OK or 202 Accepted. Any other status triggers this error, which also appends the server-provided error message decoded from the response body via decodeErrorMessage (proto.Error). It is the most informative variant of the SendMessage failure because it includes server-side detail.
Source
Thrown at internal/client/proto.go:506
// When runID is non-empty it is echoed back on the resulting
// proto.RunComplete event, giving the caller a unique correlator
// for completion detection. Pass "" when the caller does not need
// to distinguish its own turn's terminal event from any concurrent
// turn on the same session (e.g. interactive TUI usage).
func (c *Client) SendMessage(ctx context.Context, id string, sessionID, runID, prompt string, attachments ...message.Attachment) error {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/agent", id), nil, jsonBody(proto.AgentMessage{
SessionID: sessionID,
RunID: runID,
Prompt: prompt,
Attachments: proto.AttachmentsFromMessage(attachments),
}), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return fmt.Errorf("failed to send message to agent: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK && rsp.StatusCode != http.StatusAccepted {
if msg := decodeErrorMessage(rsp.Body); msg != "" {
return fmt.Errorf("failed to send message to agent: status code %d: %s", rsp.StatusCode, msg)
}
return fmt.Errorf("failed to send message to agent: status code %d", rsp.StatusCode)
}
return nil
}
// decodeErrorMessage attempts to decode the response body as a
// proto.Error and returns its message. It returns an empty string
// when the body is empty or cannot be decoded into a proto.Error
// with a non-empty message, letting callers fall back to a
// status-only error.
func decodeErrorMessage(body io.Reader) string {
var e proto.Error
if err := json.NewDecoder(body).Decode(&e); err != nil {
return ""
}
return e.Message
}View on GitHub (pinned to 7944b8e522)
Solutions
- Read the %s message suffix — it contains the server's own error explanation.
- For 404, verify the workspace and session IDs are current (re-fetch session info).
- For 401/403, refresh authentication before retrying.
- For 400, validate the prompt and attachments against the API expectations.
Example fix
// before
if err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {
return err
}
// after
if err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {
log.Printf("send failed: %v", err) // includes server message after 'status code N:'
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: verify session still exists before sending
if _, err := client.GetAgentSessionInfo(ctx, wsID, sid); err != nil {
return fmt.Errorf("session %s unavailable before send: %w", sid, err)
} Type guard
// Go: pull the server-provided message out of the composed error
func serverMessage(err error) string {
s := err.Error()
if i := strings.Index(s, "status code "); i >= 0 {
if j := strings.Index(s[i:], ": "); j >= 0 {
return s[i+j+2:]
}
}
return ""
} Try / catch
if err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {
if msg := serverMessage(err); msg != "" {
log.Printf("server rejected message: %s", msg) // actionable server detail
}
return err
} Prevention
- Always read the message after 'status code N:' — it is the server's diagnosis.
- Re-fetch session info if the session may have expired between calls.
- Validate prompts/attachments against the current API schema.
- Refresh tokens on 401 before concluding the endpoint is broken.
When it happens
Trigger: Sending a message when the session/workspace no longer exists (404), auth is rejected (401/403), the request payload is rejected (400), or the agent handler errors (500) — and the server returned a parseable error body.
Common situations: Sending to a session ID from a previous run; expired token in long-lived non-interactive jobs; server validation rejecting empty prompts or malformed attachments.
Related errors
- failed to send message to agent: status code %d
- request failed with status code: %d
- failed to refresh OAuth token: status code %d
- failed to check project init: status code %d
- failed to mark project initialized: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/df4a936316772d7f.
Report an issue: GitHub.