charmbracelet/crush · error

status code %d

Error message

status code %d

What it means

checkStatus produces this bare "status code %d" error when the response status is not accepted AND the body contains no decodable error message. The caller only learns the numeric status; the server gave no usable error payload.

Source

Thrown at internal/client/errors.go:55

)

// checkStatus returns nil when rsp's status code is one of ok
// (http.StatusOK when none are given). Otherwise it returns an error
// carrying the status code and, when the body decodes as a proto.Error,
// the server-provided message. Statuses that callers act on are wrapped
// in the matching sentinel. checkStatus may consume the response body.
func checkStatus(rsp *http.Response, ok ...int) error {
	if len(ok) == 0 {
		ok = []int{http.StatusOK}
	}
	if slices.Contains(ok, rsp.StatusCode) {
		return nil
	}
	var err error
	if msg := decodeErrorMessage(rsp.Body); msg != "" {
		err = fmt.Errorf("status code %d: %s", rsp.StatusCode, msg)
	} else {
		err = fmt.Errorf("status code %d", rsp.StatusCode)
	}
	switch rsp.StatusCode {
	case http.StatusNotFound:
		return fmt.Errorf("%w: %w", ErrNotFound, err)
	case http.StatusServiceUnavailable:
		return fmt.Errorf("%w: %w", ErrServerShuttingDown, err)
	}
	return err
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Map the numeric status to its meaning (404 = wrong endpoint/not running, 502/503 = upstream down).
  2. Verify the server is running the expected version at the configured address.
  3. Check server/proxy logs for the request to find why no error body was returned.
  4. Confirm the base URL is correct — a 404 here often means the path or port is wrong.

Example fix

// before: opaque handling
if err := checkStatus(rsp); err != nil {
	return err
}
// after: log status and inspect body directly when message is absent
if err := checkStatus(rsp); err != nil {
	log.Printf("request failed with unexplained status; check server logs and base URL: %v", err)
	return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Health-check the server before making real calls
resp, err := http.Get(baseURL + "/health")
if err != nil || resp.StatusCode != http.StatusOK {
	return errors.New("daemon is not reachable; start it before calling the client")
}

Type guard

func isUnexplainedStatusError(err error) bool {
	return err != nil && strings.HasPrefix(err.Error(), "status code ") && !strings.Contains(err.Error(), ": ")
}

Try / catch

resp, err := doRequest(ctx)
if err != nil {
	if isUnexplainedStatusError(err) {
		// No message from server: transient infra issue likely; retry with backoff
		return retryWithBackoff(ctx, 3, doRequest)
	}
	return err
}

Prevention

When it happens

Trigger: Any checkStatus-guarded call (RetireClient, CreateWorkspace, GetWorkspace, SetCurrentSession, SubscribeEvents) receiving a non-OK response with an empty, non-JSON, or message-less body — e.g. a bare 502 from a proxy, a 404 with an empty body, or a connection reset mid-response.

Common situations: The daemon is behind a load balancer returning HTML/empty errors; the server crashed without writing a JSON error; an old server version predating the error-message format; a proxy stripping response bodies.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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