charmbracelet/crush · error
timeout waiting for agent: %w
Error message
timeout waiting for agent: %w
What it means
This error is wrapped by waitForAgent (internal/cmd/run.go:459) when the 30-second readiness timeout fires while the last GetAgentInfo call itself returned an error. It means the agent never reported IsReady within the deadline AND polling kept failing, so the wrapped error is the last polling failure (connection refused, HTTP error, etc.).
Source
Thrown at internal/cmd/run.go:469
stop()
return true, fmt.Errorf("agent error: %w", e.Payload.Error)
}
return false, nil
}
// waitForAgent polls GetAgentInfo until the agent is ready, with a
// timeout.
func waitForAgent(ctx context.Context, c *client.Client, wsID string) error {
timeout := time.After(30 * time.Second)
for {
info, err := c.GetAgentInfo(ctx, wsID)
if err == nil && info.IsReady {
return nil
}
select {
case <-timeout:
if err != nil {
return fmt.Errorf("timeout waiting for agent: %w", err)
}
return fmt.Errorf("timeout waiting for agent readiness")
case <-ctx.Done():
return ctx.Err()
case <-time.After(200 * time.Millisecond):
}
}
}
// overrideModels resolves model strings and updates the workspace
// configuration via the server.
func overrideModels(
ctx context.Context,
c *client.Client,
ws *proto.Workspace,
largeModel, smallModel string,
) error {
cfg, err := c.GetConfig(ctx, ws.ID)View on GitHub (pinned to 7944b8e522)
Solutions
- Inspect the wrapped cause (%w) to see why GetAgentInfo fails (connection refused vs 401 vs 404) and fix that root issue
- Verify the server is running and reachable at the configured address before invoking crush run
- Check credentials/environment variables used by the client
- Increase the timeout or retry the run if startup is genuinely slow (resource-constrained environments)
- Confirm the workspace ID is valid
Example fix
// before
if err := runNonInteractive(...); err != nil {
fmt.Println(err)
}
// after
if err := runNonInteractive(...); err != nil {
var to *timeoutErr
if errors.As(err, &to) {
fmt.Printf("agent startup failed: %v\n", errors.Unwrap(err))
}
} Defensive patterns
Strategy: retry
Validate before calling
// Before invoking the run, check the server is reachable
conn, err := net.DialTimeout("tcp", serverAddr, 2*time.Second)
if err != nil {
return fmt.Errorf("server unreachable at %s: %w", serverAddr, err)
}
conn.Close() Try / catch
if err := runNonInteractive(...); err != nil {
if strings.Contains(err.Error(), "timeout waiting for agent") {
// log errors.Unwrap(err) for the last polling failure, then retry
}
} Prevention
- Health-check the server endpoint before starting non-interactive runs
- Validate auth credentials and workspace ID early in scripts
- Use generous timeouts in CI/containers where startup is slow
- Monitor the wrapped cause (%w) — it names the real failure
When it happens
Trigger: The client.Run non-interactive path calls runNonInteractive -> waitForAgent; GetAgentInfo(ctx, wsID) returns an error on every poll (server unreachable, auth failure, workspace ID invalid) for the full 30s timeout window.
Common situations: Server not yet started or crashed during startup; wrong server URL/port in environment; expired or missing API credentials; pointing at a workspace that was deleted; slow machine/container startup exceeding 30s.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to make request: %w
- timeout waiting for agent readiness
- empty providers list from catwalk
- failed to download from URL: %w
- failed to fetch URL: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/aa42f9ff590cbc67.
Report an issue: GitHub.