charmbracelet/crush · error · ErrNotFound
not found
Error message
not found
What it means
ErrNotFound reports that the server answered 404. For a workspace-scoped call this means the server no longer knows the workspace — it was torn down, or the server was replaced under the client — so retrying the same ID can never start succeeding again. The client must re-register (create a new workspace) rather than retry.
Source
Thrown at internal/client/errors.go:18
package client
import (
"errors"
"fmt"
"net/http"
"slices"
)
// Typed outcomes callers need to distinguish from ordinary transport
// failures. Match them with errors.Is.
var (
// ErrNotFound reports that the server answered 404. For a
// workspace-scoped call this means the server no longer knows the
// workspace — it was torn down, or the server was replaced under the
// client — so the right response is to re-register rather than retry
// the same ID, which can never start succeeding again.
ErrNotFound = errors.New("not found")
// ErrServerBusy reports that the server declined to shut down
// because it is still hosting workspaces or is midway through
// creating one. A client asking a version-mismatched server to stand
// down must keep using it instead of assuming it is going away.
ErrServerBusy = errors.New("server busy")
// ErrServerShuttingDown reports that the server refused the request
// because it has already committed to exiting. The work is not lost:
// a replacement server can be started and the request retried
// against it.
ErrServerShuttingDown = errors.New("server is shutting down")
// ErrUnsupported reports that the running server does not understand
// the request because it predates the feature. Callers must decide
// what is safe to do with an older server rather than treating the
// failure as transient.
ErrUnsupported = errors.New("unsupported by the running server")View on GitHub (pinned to 7944b8e522)
Solutions
- Re-register: create a new workspace instead of retrying the stale ID
- Match with errors.Is(err, client.ErrNotFound) and branch to re-registration logic
- Check whether the server process was replaced and reconnect to the current one
Example fix
// before
if err := client.SendMessage(ctx, wsID, msg); err != nil { return err }
// after
if err := client.SendMessage(ctx, wsID, msg); err != nil {
if errors.Is(err, client.ErrNotFound) {
wsID, err = client.CreateWorkspace(ctx, path)
if err != nil { return err }
return client.SendMessage(ctx, wsID, msg)
}
return err
} Defensive patterns
Strategy: fallback
Type guard
func isErrNotFound(err error) bool {
return errors.Is(err, client.ErrNotFound)
} Try / catch
if err := client.SendMessage(ctx, wsID, msg); err != nil {
if errors.Is(err, client.ErrNotFound) {
return reRegisterAndResend(ctx, path, msg) // never retry the old ID
}
return err
} Prevention
- Never retry a workspace-scoped call after 404 — the ID is permanently dead
- Re-register (new workspace) on every ErrNotFound instead of backing off
- Track server identity so a replaced server is detected before calls are made
When it happens
Trigger: RetireClient receiving 404; checkStatus mapping http.StatusNotFound to this sentinel for any workspace-scoped call (SendMessage, attach, session ops); runSubscription's stream endpoint vanishing; AgentReadyErr surfacing a 404 while polling agent readiness.
Common situations: The server's idle shutdown replaced the process; detach grace expired and the workspace was torn down; workspace-scoped call issued against a restarted server that lost in-memory state.
Related errors
- failed to create workspace: %v
- failed to create workspace: server kept shutting down
- client not attached
- workspace closing
- requested channels differ from the existing workspace; chann
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/8fda9434e0621871.
Report an issue: GitHub.