charmbracelet/crush · error

failed to run shell command: status code %d

Error message

failed to run shell command: status code %d

What it means

RunShellCommand requires 200 OK. Any other status produces this error with the numeric status code; the response body is not decoded. Common statuses: 404 for unknown workspace/session, 401/403 for auth, 500 for server-side execution failure.

Source

Thrown at internal/client/proto.go:537

	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.
func (c *Client) RunShellCommand(ctx context.Context, id, sessionID, command string, termWidth int) (proto.ShellCommandResponse, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/agent/sessions/%s/shell", id, sessionID), nil, jsonBody(proto.ShellCommandRequest{
		Command:   command,
		TermWidth: termWidth,
	}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return proto.ShellCommandResponse{}, fmt.Errorf("failed to run shell command: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return proto.ShellCommandResponse{}, fmt.Errorf("failed to run shell command: status code %d", rsp.StatusCode)
	}
	var resp proto.ShellCommandResponse
	if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {
		return proto.ShellCommandResponse{}, fmt.Errorf("failed to decode shell command response: %w", err)
	}
	return resp, nil
}

// GetAgentSessionInfo retrieves the agent session info for a workspace.
func (c *Client) GetAgentSessionInfo(ctx context.Context, id string, sessionID string) (*proto.AgentSession, error) {
	rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/agent/sessions/%s", id, sessionID), nil, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get session agent info: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to get session agent info: status code %d", rsp.StatusCode)
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. For 404, re-acquire a valid session ID (the old one may have been closed).
  2. For 401/403, refresh credentials.
  3. For 5xx, inspect server logs; retry with backoff if the command is idempotent.
  4. Confirm the endpoint exists on the deployed server version.

Example fix

// before
if _, err := client.RunShellCommand(ctx, wsID, sid, cmd, w); err != nil {
    return err
}
// after
if _, err := client.RunShellCommand(ctx, wsID, sid, cmd, w); err != nil {
    if strings.Contains(err.Error(), "status code 404") {
        return fmt.Errorf("session %s closed; re-open session before running commands", sid)
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Go: ensure the session exists before running commands
if _, err := client.GetAgentSessionInfo(ctx, wsID, sid); err != nil {
    return fmt.Errorf("cannot run command in session %s: %w", sid, err)
}

Type guard

// Go: extract and branch on status code
func statusCode(err error) int {
    var code int
    fmt.Sscanf(err.Error(), "failed to run shell command: status code %d", &code)
    return code
}

Try / catch

_, err := client.RunShellCommand(ctx, wsID, sid, cmd, w)
if err != nil && statusCode(err) >= 500 {
    time.Sleep(2 * time.Second)
    _, err = client.RunShellCommand(ctx, wsID, sid, cmd, w) // only retry idempotent commands
}
if err != nil { return err }

Prevention

When it happens

Trigger: Executing a shell command against a non-existent session (404), rejected auth (401/403), or a handler that failed before producing a result (5xx).

Common situations: Session ended/closed on the server while the client still holds its id; token expiry in long tool-running sessions; server error in the command executor.

Related errors


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