charmbracelet/crush · error
failed to decode shell command response: %w
Error message
failed to decode shell command response: %w
What it means
After a 200 response, RunShellCommand decodes the body into proto.ShellCommandResponse. This error wraps any JSON decode failure (empty body, truncated response, wrong content type, schema mismatch) via %w. The command may actually have run server-side even though the result could not be parsed.
Source
Thrown at internal/client/proto.go:541
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)
}
var info proto.AgentSession
if err := json.NewDecoder(rsp.Body).Decode(&info); err != nil {
return nil, fmt.Errorf("failed to decode session agent info: %w", err)
}View on GitHub (pinned to 7944b8e522)
Solutions
- Check for client/server version skew and align versions — schema mismatch is the most common cause.
- Inspect the raw response body (debug proxy or server logs) to see what was actually returned.
- Treat the command as possibly-executed; avoid blind retries of non-idempotent commands.
- Verify no intermediary proxy is modifying or truncating the response body.
Example fix
// before: unsafe retry of any decode failure
if err != nil { retry(cmd) }
// after: decode failure may mean the command already ran
resp, err := client.RunShellCommand(ctx, wsID, sid, cmd, w)
if err != nil && strings.Contains(err.Error(), "failed to decode shell command response") {
log.Printf("result lost for %q; command may have executed", cmd)
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
// Go: check client/server protocol version compatibility before calls
if clientVersion != serverAPIVersion {
return fmt.Errorf("client %s vs server %s: response schema may mismatch", clientVersion, serverAPIVersion)
} Type guard
// Go: identify decode failures specifically
func isDecodeFailure(err error) bool {
return strings.Contains(err.Error(), "failed to decode shell command response")
} Try / catch
resp, err := client.RunShellCommand(ctx, wsID, sid, cmd, w)
if err != nil {
if isDecodeFailure(err) {
// command may have executed; verify out-of-band before retrying
return fmt.Errorf("result unreadable for %q; check server state before rerunning", cmd)
}
return err
} Prevention
- Do not blindly retry after a decode failure — the command may have already executed.
- Pin matching client/server versions; deploy them together.
- Disable any proxy that rewrites, gzips, or truncates response bodies.
- Inspect raw responses once (debug proxy) to identify schema drift early.
When it happens
Trigger: Server returns 200 with an empty, truncated, or non-JSON body, or a ShellCommandResponse schema the client cannot unmarshal (version skew between client and server types).
Common situations: Proxy gzipping/stripping the body; server/client version mismatch after an upgrade; server crash after starting the command but before writing the response.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode skills: %w
- failed to decode skill response: %w
- failed to decode MCP resource: %w
- failed to decode MCP prompts: %w
- failed to decode workspace: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/ff213e7da42cbfe0.
Report an issue: GitHub.