{"record":{"id":"af74c85e21036480","repo":"charmbracelet/crush","slug":"failed-to-decode-grant-permission-response-w","errorCode":null,"errorMessage":"failed to decode grant permission response: %w","messagePattern":"failed to decode grant permission response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":691,"sourceCode":"}\n\n// GrantPermission grants a permission on a workspace. The returned\n// bool reports whether this call resolved the pending request (true)\n// or found it already resolved by a previous caller (false). A false\n// value is not an error — it just means another subscriber resolved\n// the same request first.\nfunc (c *Client) GrantPermission(ctx context.Context, id string, req proto.PermissionGrant) (bool, error) {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/permissions/grant\", id), nil, jsonBody(req), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to grant permission: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn false, fmt.Errorf(\"failed to grant permission: status code %d\", rsp.StatusCode)\n\t}\n\tvar resp proto.PermissionGrantResponse\n\tif err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {\n\t\treturn false, fmt.Errorf(\"failed to decode grant permission response: %w\", err)\n\t}\n\treturn resp.Resolved, nil\n}\n\n// AnswerQuestionBatch submits answers for a batch question on a\n// workspace. Returns true if this call resolved the pending\n// request, false if already resolved by another caller.\nfunc (c *Client) AnswerQuestionBatch(ctx context.Context, id string, req proto.QuestionAnswer) (bool, error) {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/questions/answer\", id), nil, jsonBody(req), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to answer question batch: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn false, fmt.Errorf(\"failed to answer question batch: status code %d\", rsp.StatusCode)\n\t}\n\tvar resp proto.QuestionAnswerResponse\n\tif err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L673-L709","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isDecodeError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"failed to decode grant permission response\")\n}","tryCatchPattern":"resolved, err := client.GrantPermission(ctx, wsID, grant)\nif isDecodeError(err) {\n\t// 200 with unparseable body: capture for diagnosis, treat as unknown outcome\n\tlog.Error(\"grant response undecodable; outcome unknown\", \"err\", err)\n\treturn errUnknownOutcome\n}","preventionTips":["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"],"tags":["json","decoding","http-client","permissions"],"backgroundTag":"json-decode-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}