charmbracelet/crush · error

failed to update agent: status code %d

Error message

failed to update agent: status code %d

What it means

UpdateAgent expects the server to reply 200 OK to /workspaces/{id}/agent/update. Any other HTTP status (404, 401, 500, etc.) produces this error carrying the numeric status code. Unlike the %w variant, the response body is not decoded, so server-side detail is not included.

Source

Thrown at internal/client/proto.go:481

	if err := checkStatus(rsp); err != nil {
		return nil, fmt.Errorf("failed to get agent status: %w", err)
	}
	var info proto.AgentInfo
	if err := json.NewDecoder(rsp.Body).Decode(&info); err != nil {
		return nil, fmt.Errorf("failed to decode agent status: %w", err)
	}
	return &info, nil
}

// UpdateAgent triggers an agent model update on the server.
func (c *Client) UpdateAgent(ctx context.Context, id string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/agent/update", id), nil, nil, nil)
	if err != nil {
		return fmt.Errorf("failed to update agent: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to update agent: status code %d", rsp.StatusCode)
	}
	return nil
}

// SendMessage sends a message to the agent for a workspace.
//
// 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"}})

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Log the status code and check the server logs for the matching request.
  2. For 404, re-list workspaces to confirm the id still exists before retrying.
  3. For 401/403, refresh credentials/auth token and retry.
  4. For 5xx, retry with backoff; if persistent, check client/server version compatibility.

Example fix

// before
if err := client.UpdateAgent(ctx, wsID); err != nil {
    return err
}
// after
if err := client.UpdateAgent(ctx, wsID); err != nil {
    if strings.Contains(err.Error(), "status code 404") {
        return fmt.Errorf("workspace %s no longer exists", wsID)
    }
    return err
}
Defensive patterns

Strategy: fallback

Validate before calling

// Go: confirm workspace exists via another endpoint before updating
infos, err := client.ListWorkspaces(ctx)
if err != nil { return err }
var found bool
for _, w := range infos { if w.ID == workspaceID { found = true } }
if !found { return fmt.Errorf("workspace %q not found", workspaceID) }

Type guard

// Go: extract the status code from the error string
func statusCodeFrom(err error) (int, bool) {
    var code int
    n, _ := fmt.Sscanf(err.Error(), "failed to update agent: status code %d", &code)
    return code, n == 1
}

Try / catch

if err := client.UpdateAgent(ctx, id); err != nil {
    if code, ok := statusCodeFrom(err); ok {
        switch {
        case code == 401 || code == 403:
            return refreshAuthAndRetry()
        case code == 404:
            return fmt.Errorf("workspace %s missing", id)
        default:
            return retryWithBackoff(func() error { return client.UpdateAgent(ctx, id) })
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling UpdateAgent on a workspace id that does not exist (404), with missing/invalid auth (401/403), when the server-side update handler fails (500), or when an older server lacks the /agent/update route.

Common situations: Typo'd or recycled workspace id; token expired mid-session; client/server version skew where the route was renamed or removed; server bug during model override.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/821c593045401dcb. Report an issue: GitHub.