{"record":{"id":"27f703021a0c016a","repo":"charmbracelet/crush","slug":"unexpected-status-code-d-27f703","errorCode":null,"errorMessage":"unexpected status code: %d","messagePattern":"unexpected status code: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/hyper.go","lineNumber":174,"sourceCode":"\t}\n\n\tclient := &http.Client{Timeout: 30 * time.Second}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"failed to make request: %w\", err)\n\t}\n\tdefer resp.Body.Close() //nolint:errcheck\n\n\tif resp.StatusCode == http.StatusNotModified {\n\t\treturn result, catwalk.ErrNotModified\n\t}\n\n\tif resp.StatusCode == http.StatusUnauthorized {\n\t\treturn result, errUnauthorized\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn result, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\tif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {\n\t\treturn result, fmt.Errorf(\"failed to decode response: %w\", err)\n\t}\n\n\treturn result, nil\n}\n\n// errUnauthorized is a sentinel for HTTP 401 responses from the Hyper API.\nvar errUnauthorized = errors.New(\"unauthorized\")\n\n// isHTTPUnauthorized reports whether err is or wraps errUnauthorized.\nfunc isHTTPUnauthorized(err error) bool {\n\treturn errors.Is(err, errUnauthorized)\n}\n","sourceCodeStart":156,"sourceCodeEnd":191,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/hyper.go#L156-L191","documentation":"Returned by doGet when the Hyper API responds with a status code other than 200, 304, or 401. 304 and 401 have dedicated handling (ErrNotModified and errUnauthorized), so anything else — 500, 502, 503, 404, 429 — falls into this generic error.","triggerScenarios":"Calling config.Get when the Hyper API returns e.g. HTTP 500 (server error), 502/503 (gateway outage), 404 (wrong path/endpoint version), or 429 (rate limited).","commonSituations":"Hyper API outage or degraded service; client pointing at a stale/moved endpoint; hitting rate limits from frequent polling; API version changes removing the endpoint.","solutions":["Check the Hyper API status/uptime for outages before debugging client code.","Log the status code and response body to identify whether it's 4xx (client) or 5xx (server).","For 429, add backoff/retry with respect for Retry-After headers.","Verify the endpoint URL and API version are current."],"exampleFix":"// before\ncfg, err := config.Get()\n// after: retry on transient server errors\nfor i := 0; i < 3; i++ {\n\tcfg, err = config.Get()\n\tif err == nil || !strings.Contains(err.Error(), \"unexpected status code: 5\") {\n\t\tbreak\n\t}\n\ttime.Sleep(time.Duration(1<<i) * time.Second)\n}","handlingStrategy":"retry","validationCode":"// no pre-call validation possible; probe endpoint health instead\n// curl -s -o /dev/null -w '%{http_code}' https://hyper-endpoint/health","typeGuard":"var scErr *statusCodeError\nif errors.As(err, &scErr) && scErr.Code >= 500 { /* transient */ }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"unexpected status code\") {\n\t// log code, retry on 5xx/429 with backoff, surface 4xx to user\n}","preventionTips":["Monitor provider status pages for outages.","Back off on 429 and honor Retry-After.","Keep endpoint URLs and API versions up to date.","Distinguish 4xx (fix request) from 5xx (retry) before reacting."],"tags":["http","api","status-code"],"backgroundTag":"unexpected-http-status","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}