{"record":{"id":"b0a5a348284b14bd","repo":"charmbracelet/crush","slug":"failed-to-grant-permission-status-code-d","errorCode":null,"errorMessage":"failed to grant permission: status code %d","messagePattern":"failed to grant permission: status code (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":687,"sourceCode":"\tif err := json.NewDecoder(rsp.Body).Decode(&sessions); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode sessions: %w\", err)\n\t}\n\treturn sessions, nil\n}\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 {","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L669-L705","documentation":"GrantPermission expects the server to answer /workspaces/{id}/permissions/grant with HTTP 200. Any other status code (401, 403, 404, 500, etc.) produces this error. It indicates the server received and rejected the request, so the permission grant did not succeed.","triggerScenarios":"POSTing to /workspaces/{id}/permissions/grant when the workspace id does not exist (404), the caller lacks authorization (401/403), the request is malformed (400), or the server errors (5xx).","commonSituations":"Stale or wrong workspace id after a session restart; expired auth token/credentials; server version mismatch where the endpoint moved; server-side bug returning 500.","solutions":["Log/inspect the actual status code in the wrapped message and map it: 401/403 → re-authenticate, 404 → verify the workspace id, 5xx → check server logs.","Confirm the workspace id passed to GrantPermission is current and valid.","Refresh credentials or re-login if the status is 401/403.","Check client/server version compatibility; upgrade the client if the endpoint contract changed."],"exampleFix":"// before\nresolved, err := client.GrantPermission(ctx, wsID, grant)\n// after\nresolved, err := client.GrantPermission(ctx, wsID, grant)\nif err != nil && strings.Contains(err.Error(), \"status code 404\") {\n\t// refresh workspace id from server listing before retrying\n}","handlingStrategy":"try-catch","validationCode":"// Validate workspace id shape and auth presence before calling\nif wsID == \"\" || token == \"\" {\n\treturn errors.New(\"workspace id and credentials are required before granting permissions\")\n}","typeGuard":null,"tryCatchPattern":"resolved, err := client.GrantPermission(ctx, wsID, grant)\nvar statusErr *statusError // or inspect embedded \"status code %d\" text\nif err != nil {\n\tswitch {\n\tcase strings.Contains(err.Error(), \"status code 401\"), strings.Contains(err.Error(), \"status code 403\"):\n\t\treturn reauthenticateAndRetry(ctx, wsID, grant)\n\tcase strings.Contains(err.Error(), \"status code 404\"):\n\t\treturn fmt.Errorf(\"workspace %q not found: %w\", wsID, err)\n\tdefault:\n\t\treturn err\n\t}\n}","preventionTips":["Fetch the workspace id from the server rather than hardcoding it","Refresh auth tokens before expiry","Pin compatible client/server versions","Log full error text so the embedded status code is never lost"],"tags":["http-client","http-status","permissions","api"],"backgroundTag":"http-non-200-response","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}