charmbracelet/crush · error

failed to decode response: %w

Error message

failed to decode response: %w

What it means

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.

Source

Thrown at internal/config/hyper.go:178

	if err != nil {
		return result, fmt.Errorf("failed to make request: %w", err)
	}
	defer resp.Body.Close() //nolint:errcheck

	if resp.StatusCode == http.StatusNotModified {
		return result, catwalk.ErrNotModified
	}

	if resp.StatusCode == http.StatusUnauthorized {
		return result, errUnauthorized
	}

	if resp.StatusCode != http.StatusOK {
		return result, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return result, fmt.Errorf("failed to decode response: %w", err)
	}

	return result, nil
}

// errUnauthorized is a sentinel for HTTP 401 responses from the Hyper API.
var errUnauthorized = errors.New("unauthorized")

// isHTTPUnauthorized reports whether err is or wraps errUnauthorized.
func isHTTPUnauthorized(err error) bool {
	return errors.Is(err, errUnauthorized)
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Capture and inspect the raw response body to see what was actually returned.
  2. Check for intercepting proxies or captive portals mangling the response.
  3. Update the client if the Hyper API response schema changed.
  4. Retry — truncated bodies from transient network issues usually succeed on retry.

Example fix

// before
cfg, err := config.Get()
// after: inspect and fall back
if err != nil && strings.Contains(err.Error(), "failed to decode response") {
	log.Printf("hyper response undecodable, using cached providers")
	cfg = loadCachedConfig()
}
Defensive patterns

Strategy: fallback

Validate before calling

// optionally probe with a raw request first
resp, _ := http.Get(endpoint)
ct := resp.Header.Get("Content-Type")
ok := strings.HasPrefix(ct, "application/json")

Type guard

func isDecodeError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "failed to decode response")
}

Try / catch

if err != nil && isDecodeError(err) {
	cfg = loadCachedConfig() // fall back to last good snapshot
}

Prevention

When it happens

Trigger: 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).

Common situations: Captive portals or intercepting proxies returning HTML with 200; API schema drift between client struct and server response; truncated responses from flaky connections.

Understand the failure class

Related errors


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