charmbracelet/crush · error
failed to decode project init response: %w
Error message
failed to decode project init response: %w
What it means
After a 200 response, ProjectNeedsInitialization decodes the JSON body expecting {"needs_init": bool}. A body that is empty, malformed, or has the wrong shape produces this error. It indicates a server/client contract mismatch rather than a network problem.
Source
Thrown at internal/client/config.go:180
return nil
}
// ProjectNeedsInitialization checks if the project needs
// initialization.
func (c *Client) ProjectNeedsInitialization(ctx context.Context, id string) (bool, error) {
rsp, err := c.get(ctx, fmt.Sprintf("/workspaces/%s/project/needs-init", id), nil, nil)
if err != nil {
return false, fmt.Errorf("failed to check project init: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return false, fmt.Errorf("failed to check project init: status code %d", rsp.StatusCode)
}
var result struct {
NeedsInit bool `json:"needs_init"`
}
if err := json.NewDecoder(rsp.Body).Decode(&result); err != nil {
return false, fmt.Errorf("failed to decode project init response: %w", err)
}
return result.NeedsInit, nil
}
// MarkProjectInitialized marks the project as initialized on the
// server.
func (c *Client) MarkProjectInitialized(ctx context.Context, id string) error {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/project/init", id), nil, nil, nil)
if err != nil {
return fmt.Errorf("failed to mark project initialized: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to mark project initialized: status code %d", rsp.StatusCode)
}
return nil
}
View on GitHub (pinned to 7944b8e522)
Solutions
- Dump the raw response body to see what the server actually returned
- Check client/server version compatibility for the needs_init field
- Bypass or reconfigure any proxy that could inject HTML into the response
- Upgrade the client or server so both agree on the JSON contract
Example fix
// before
needsInit, err := client.ProjectNeedsInitialization(ctx, id)
if err != nil {
return err
}
// after
needsInit, err := client.ProjectNeedsInitialization(ctx, id)
if err != nil {
log.Printf("needs-init decode failed (server contract mismatch?): %v", err)
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
// Verify the server speaks JSON before trusting decoded bodies
resp, err := http.Get(client.BaseURL() + "/healthz")
if err != nil {
return err
}
ct := resp.Header.Get("Content-Type")
resp.Body.Close()
if !strings.HasPrefix(ct, "application/json") {
return fmt.Errorf("server returned non-JSON content type: %s", ct)
} Type guard
func looksLikeJSON(b []byte) bool {
t := bytes.TrimSpace(b)
return len(t) > 0 && (t[0] == '{' || t[0] == '[')
} Try / catch
needsInit, err := c.ProjectNeedsInitialization(ctx, id)
if err != nil {
if strings.Contains(err.Error(), "failed to decode") {
// contract mismatch: log payload, fall back to prompting the user
return fallbackInteractiveInit()
}
return err
} Prevention
- Assert Content-Type: application/json on API responses in server tests
- Pin compatible client/server versions; bump them together
- Bypass or correctly configure proxies that can inject HTML with 200 status
- Fuzz or golden-test response payloads when changing the API schema
When it happens
Trigger: Calling ProjectNeedsInitialization when the response body is not decodable JSON: empty body from a misconfigured proxy, HTML error page injected by an auth wall, or a server version returning a different field name/type for needs_init.
Common situations: Reverse proxy or captive portal returning HTML; server/client version skew after an API field rename; gzip/compression misconfiguration producing garbage bytes; server returning 200 with an empty body on internal errors.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode init prompt response: %w
- failed to decode session agent info: %w
- failed to decode grant permission response: %w
- failed to decode answer question batch response: %w
- failed to decode cancel question batch response: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/84e7c521afa9ccbc.
Report an issue: GitHub.