charmbracelet/crush · error
failed to run shell command: %w
Error message
failed to run shell command: %w
What it means
RunShellCommand posts to /workspaces/{id}/agent/sessions/{sid}/shell to execute a command on the agent side. This error wraps a transport failure from c.post (unreachable server, connection reset, timeout, request-build failure) before the response status is checked, with the cause preserved via %w.
Source
Thrown at internal/client/proto.go:533
// 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.
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)
}View on GitHub (pinned to 7944b8e522)
Solutions
- Verify server availability and base URL before invoking.
- Check the wrapped cause to distinguish timeout vs refused connection.
- For long-running commands, confirm the client timeout exceeds the expected command duration.
- Retry transient network failures with backoff.
Example fix
// before
resp, err := client.RunShellCommand(ctx, wsID, sid, cmd, width)
// after
resp, err := client.RunShellCommand(ctx, wsID, sid, cmd, width)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("command %q timed out: %w", cmd, err)
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: guard inputs and context before running a remote command
if command == "" { return fmt.Errorf("command is empty") }
if termWidth <= 0 { termWidth = 80 }
if err := ctx.Err(); err != nil { return err } Type guard
// Go: classify the wrapped transport failure
func classifyTransport(err error) string {
switch {
case errors.Is(err, context.DeadlineExceeded): return "timeout"
case errors.Is(err, syscall.ECONNREFUSED): return "refused"
default: return "other"
}
} Try / catch
resp, err := client.RunShellCommand(ctx, wsID, sid, cmd, width)
if err != nil {
if classifyTransport(err) == "timeout" {
log.Printf("command %q may still be running server-side", cmd)
}
return err
} Prevention
- Set the context timeout well above expected command runtime.
- Confirm the session is alive before executing remote commands.
- On timeout, check server-side process state — the command may have run.
- Keep client/server protocol versions aligned.
When it happens
Trigger: Calling RunShellCommand when the server is unreachable, the connection drops, the request times out, or the JSON body (command + termWidth) cannot be sent.
Common situations: Running remote shell commands against a server that just exited; network partition mid-session; very large output/long-running command hitting a client timeout.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- failed to make request: %w
- failed to refresh OAuth token: %w
- failed to check project init: %w
- failed to mark project initialized: %w
- failed to get init prompt: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/316dc698033f0ba5.
Report an issue: GitHub.