charmbracelet/crush · error
failed to decode grant permission response: %w
Error message
failed to decode grant permission response: %w
What it means
After a 200 response from /workspaces/{id}/permissions/grant, GrantPermission decodes the body into proto.PermissionGrantResponse. This error wraps a JSON decode failure: empty body, non-JSON body (HTML error page from a proxy), or a body whose shape does not match the response struct. The grant outcome is unknown because the Resolved field could not be read.
Source
Thrown at internal/client/proto.go:691
}
// GrantPermission grants a permission on a workspace. The returned
// bool reports whether this call resolved the pending request (true)
// or found it already resolved by a previous caller (false). A false
// value is not an error — it just means another subscriber resolved
// the same request first.
func (c *Client) GrantPermission(ctx context.Context, id string, req proto.PermissionGrant) (bool, error) {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/permissions/grant", id), nil, jsonBody(req), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return false, fmt.Errorf("failed to grant permission: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return false, fmt.Errorf("failed to grant permission: status code %d", rsp.StatusCode)
}
var resp proto.PermissionGrantResponse
if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {
return false, fmt.Errorf("failed to decode grant permission response: %w", err)
}
return resp.Resolved, nil
}
// AnswerQuestionBatch submits answers for a batch question on a
// workspace. Returns true if this call resolved the pending
// request, false if already resolved by another caller.
func (c *Client) AnswerQuestionBatch(ctx context.Context, id string, req proto.QuestionAnswer) (bool, error) {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/questions/answer", id), nil, jsonBody(req), http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return false, fmt.Errorf("failed to answer question batch: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
return false, fmt.Errorf("failed to answer question batch: status code %d", rsp.StatusCode)
}
var resp proto.QuestionAnswerResponse
if err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {View on GitHub (pinned to 7944b8e522)
Solutions
- Inspect the raw response body (curl the endpoint with the same payload) to see what was actually returned.
- Verify client and server versions match so proto.PermissionGrantResponse matches the server's response schema.
- Check for reverse proxies/gateways mangling the response and bypass or fix them.
- Retry the call; if deterministic, capture a wire dump and file a bug against the server.
Defensive patterns
Strategy: try-catch
Type guard
func isDecodeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to decode grant permission response")
} Try / catch
resolved, err := client.GrantPermission(ctx, wsID, grant)
if isDecodeError(err) {
// 200 with unparseable body: capture for diagnosis, treat as unknown outcome
log.Error("grant response undecodable; outcome unknown", "err", err)
return errUnknownOutcome
} Prevention
- Keep client and server on matching versions so response schemas align
- Bypass or fix proxies that rewrite response bodies
- Enable response-body logging to debug decode failures quickly
- Retry once on decode failure before surfacing to the user
When it happens
Trigger: Server returns 200 with an empty/truncated body; an intermediary (reverse proxy, gateway) intercepts and returns HTML; the server serializes a different response schema than proto.PermissionGrantResponse.
Common situations: Proxy or load balancer returning an HTML error page with 200; server/client version skew changing the response JSON; gzip/encoding issues; server bug writing no body.
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 session agent info: %w
- failed to decode answer question batch response: %w
- failed to decode cancel question batch response: %w
- failed to decode response: %w
- failed to decode project init response: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/af74c85e21036480.
Report an issue: GitHub.