charmbracelet/crush · error
failed to send message to agent: status code %d
Error message
failed to send message to agent: status code %d
What it means
Fallback variant of the SendMessage status error: the server returned a non-200/202 status and decodeErrorMessage returned an empty string (empty, malformed, or non-JSON body), so only the numeric status code is reported. It indicates the server failed without a usable error body.
Source
Thrown at internal/client/proto.go:508
// 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
}
// RunShellCommand runs a shell command in the workspace without triggering the agent.View on GitHub (pinned to 7944b8e522)
Solutions
- Treat the raw status as the only signal: 502/503/504 imply proxy or server availability problems — check upstream health.
- Capture the response body separately (via a debugging proxy or server logs) since the client discards it here.
- Retry with backoff for 5xx; fix auth for 401/403.
- Check whether an ingress/proxy sits between client and server and is masking the real response.
Example fix
// before: assuming err contains server detail
log.Fatalf("send: %v", err) // only 'status code 502'
// after: probe server health on opaque gateway errors
if strings.Contains(err.Error(), "status code 50") {
if healthErr := probeHealth(ctx); healthErr != nil {
return fmt.Errorf("server unhealthy: %w (send failed: %v)", healthErr, err)
}
} Defensive patterns
Strategy: fallback
Validate before calling
// Go: verify direct server reachability, bypassing any proxy assumption
resp, err := http.Get(serverBaseURL + "/health")
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("server unreachable via configured base URL")
} Type guard
// Go: detect opaque gateway statuses
func isGatewayStatus(err error) bool {
s := err.Error()
return strings.Contains(s, "status code 502") ||
strings.Contains(s, "status code 503") ||
strings.Contains(s, "status code 504")
} Try / catch
if err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {
if isGatewayStatus(err) {
return fmt.Errorf("gateway/proxy in front of the server failed: %w", err)
}
return err
} Prevention
- Audit proxies/load balancers between client and server; HTML 502 pages cause this opaque error.
- Ask the server team to always emit a JSON error body so decodeErrorMessage can extract detail.
- For 5xx, retry with exponential backoff.
- Correlate with server-side request logs since the client discards the body here.
When it happens
Trigger: Non-200/202 response whose body is empty, HTML (e.g. a reverse-proxy 502 page), truncated, or not decodable as proto.Error.
Common situations: Load balancer or proxy returning 502/503/504 with HTML pages; server crash before writing a JSON error; request rejected at the auth layer with an empty body.
Related errors
- failed to send message to agent: status code %d: %s
- failed to refresh OAuth token: status code %d
- failed to check project init: status code %d
- failed to mark project initialized: status code %d
- failed to get init prompt: status code %d
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/1e3c5058bf2117b8.
Report an issue: GitHub.