{"record":{"id":"df4a936316772d7f","repo":"charmbracelet/crush","slug":"failed-to-send-message-to-agent-status-code-d","errorCode":null,"errorMessage":"failed to send message to agent: status code %d: %s","messagePattern":"failed to send message to agent: status code (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":506,"sourceCode":"// When runID is non-empty it is echoed back on the resulting\n// proto.RunComplete event, giving the caller a unique correlator\n// for completion detection. Pass \"\" when the caller does not need\n// to distinguish its own turn's terminal event from any concurrent\n// turn on the same session (e.g. interactive TUI usage).\nfunc (c *Client) SendMessage(ctx context.Context, id string, sessionID, runID, prompt string, attachments ...message.Attachment) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/agent\", id), nil, jsonBody(proto.AgentMessage{\n\t\tSessionID:   sessionID,\n\t\tRunID:       runID,\n\t\tPrompt:      prompt,\n\t\tAttachments: proto.AttachmentsFromMessage(attachments),\n\t}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to send message to agent: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK && rsp.StatusCode != http.StatusAccepted {\n\t\tif msg := decodeErrorMessage(rsp.Body); msg != \"\" {\n\t\t\treturn fmt.Errorf(\"failed to send message to agent: status code %d: %s\", rsp.StatusCode, msg)\n\t\t}\n\t\treturn fmt.Errorf(\"failed to send message to agent: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// decodeErrorMessage attempts to decode the response body as a\n// proto.Error and returns its message. It returns an empty string\n// when the body is empty or cannot be decoded into a proto.Error\n// with a non-empty message, letting callers fall back to a\n// status-only error.\nfunc decodeErrorMessage(body io.Reader) string {\n\tvar e proto.Error\n\tif err := json.NewDecoder(body).Decode(&e); err != nil {\n\t\treturn \"\"\n\t}\n\treturn e.Message\n}","sourceCodeStart":488,"sourceCodeEnd":524,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L488-L524","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {\n    return err\n}\n// after\nif err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {\n    log.Printf(\"send failed: %v\", err) // includes server message after 'status code N:'\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Go: verify session still exists before sending\nif _, err := client.GetAgentSessionInfo(ctx, wsID, sid); err != nil {\n    return fmt.Errorf(\"session %s unavailable before send: %w\", sid, err)\n}","typeGuard":"// Go: pull the server-provided message out of the composed error\nfunc serverMessage(err error) string {\n    s := err.Error()\n    if i := strings.Index(s, \"status code \"); i >= 0 {\n        if j := strings.Index(s[i:], \": \"); j >= 0 {\n            return s[i+j+2:]\n        }\n    }\n    return \"\"\n}","tryCatchPattern":"if err := client.SendMessage(ctx, wsID, sid, runID, prompt, nil); err != nil {\n    if msg := serverMessage(err); msg != \"\" {\n        log.Printf(\"server rejected message: %s\", msg) // actionable server detail\n    }\n    return err\n}","preventionTips":["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."],"tags":["http-status","message-send","server-error"],"backgroundTag":"unexpected-http-status","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}