{"record":{"id":"43aedb47f418d5f7","repo":"charmbracelet/crush","slug":"failed-to-decode-response-w-43aedb","errorCode":null,"errorMessage":"failed to decode response: %w","messagePattern":"failed to decode response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/hyper.go","lineNumber":178,"sourceCode":"\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":160,"sourceCodeEnd":191,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/config/hyper.go#L160-L191","documentation":"Returned by doGet when the Hyper API responds with HTTP 200 but the body cannot be decoded into the expected result struct via json.Decoder. Indicates malformed, truncated, or structurally incompatible JSON from the server.","triggerScenarios":"Calling config.Get when the Hyper API returns a 200 response whose body is not valid JSON or does not match the expected schema (e.g. a proxy returning an HTML error page with status 200, or an API schema change).","commonSituations":"Captive portals or intercepting proxies returning HTML with 200; API schema drift between client struct and server response; truncated responses from flaky connections.","solutions":["Capture and inspect the raw response body to see what was actually returned.","Check for intercepting proxies or captive portals mangling the response.","Update the client if the Hyper API response schema changed.","Retry — truncated bodies from transient network issues usually succeed on retry."],"exampleFix":"// before\ncfg, err := config.Get()\n// after: inspect and fall back\nif err != nil && strings.Contains(err.Error(), \"failed to decode response\") {\n\tlog.Printf(\"hyper response undecodable, using cached providers\")\n\tcfg = loadCachedConfig()\n}","handlingStrategy":"fallback","validationCode":"// optionally probe with a raw request first\nresp, _ := http.Get(endpoint)\nct := resp.Header.Get(\"Content-Type\")\nok := strings.HasPrefix(ct, \"application/json\")","typeGuard":"func isDecodeError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"failed to decode response\")\n}","tryCatchPattern":"if err != nil && isDecodeError(err) {\n\tcfg = loadCachedConfig() // fall back to last good snapshot\n}","preventionTips":["Cache the last successfully decoded response for fallback.","Check Content-Type to catch proxies returning HTML.","Pin/verify API version compatibility when upgrading.","Log the raw body on decode failure for diagnosis."],"tags":["json","decoding","api"],"backgroundTag":"json-decode-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}