{"record":{"id":"5eadff9595e5879b","repo":"charmbracelet/crush","slug":"failed-to-decode-cancel-question-batch-response","errorCode":null,"errorMessage":"failed to decode cancel question batch response: %w","messagePattern":"failed to decode cancel question batch response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":729,"sourceCode":"\t}\n\treturn resp.Resolved, nil\n}\n\n// CancelQuestionBatch cancels the pending question batch on a\n// workspace. Returns true if a question was cancelled, false if\n// none was pending.\nfunc (c *Client) CancelQuestionBatch(ctx context.Context, id string) (bool, error) {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/questions/cancel\", id), nil, nil, http.Header{})\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to cancel 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 cancel 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 {\n\t\treturn false, fmt.Errorf(\"failed to decode cancel question batch response: %w\", err)\n\t}\n\treturn resp.Resolved, nil\n}\n\n// SetPermissionsSkipRequests sets the skip-requests flag for a workspace.\nfunc (c *Client) SetPermissionsSkipRequests(ctx context.Context, id string, skip bool) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/permissions/skip\", id), nil, jsonBody(proto.PermissionSkipRequest{Skip: skip}), http.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to set permissions skip requests: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"failed to set permissions skip requests: status code %d\", rsp.StatusCode)\n\t}\n\treturn nil\n}\n\n// GetPermissionsSkipRequests retrieves the skip-requests flag for a workspace.","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L711-L747","documentation":"After a 200 from /workspaces/{id}/questions/cancel, CancelQuestionBatch decodes proto.QuestionAnswerResponse to learn whether a question was cancelled (Resolved). A decode failure — empty body, non-JSON proxy page, schema mismatch — raises this error, leaving the cancel outcome ambiguous.","triggerScenarios":"Server returns 200 with an empty or non-JSON body; an intermediary substitutes an HTML page; server response shape changed relative to proto.QuestionAnswerResponse.","commonSituations":"Proxy/gateway HTML interstitial; truncated response on unstable connections; client/server version skew after an upgrade.","solutions":["Inspect the raw 200 response body with curl or a logging proxy.","Align client and server versions so proto.QuestionAnswerResponse matches.","Check reverse proxy/gateway behavior that could rewrite responses.","Retry the cancel; note a false Resolved just means nothing was pending."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isCancelDecodeError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"failed to decode cancel question batch response\")\n}","tryCatchPattern":"ok, err := client.CancelQuestionBatch(ctx, wsID)\nif isCancelDecodeError(err) {\n\t// 200 but unreadable body; retry once — cancel is idempotent\n\treturn retryCancel(ctx, wsID, 1)\n}","preventionTips":["Keep client/server versions aligned","Log raw 200 bodies when decode fails to identify proxy tampering","Retry cancels — Resolved makes them idempotent","Test cancel flows through your full proxy chain, not just direct"],"tags":["json","decoding","http-client","questions"],"backgroundTag":"json-decode-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}