feat(base): classify Base resource permission denials as typed errors - #2125
Conversation
Map the Base API 91403 response code to a typed PermissionError instead of a generic API error. The error carries the resolved identity, a permission-denied subtype, code, and log ID, plus an actionable hint that tells AI agents to ask the Base owner for access rather than repeatedly running auth login or switching identities for a resource-level denial. Co-authored-by: TRAE CLI <noreply@bytedance.com>
📝 WalkthroughWalkthroughBase error handling now classifies response code 91403 as a permission denial, preserving identity, hints, Base code, and trimmed log ID. Tests verify the typed authorization error and its contextual fields. ChangesBase permission classification
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@shortcuts/base/base_errors_test.go`:
- Around line 121-151: The test
TestHandleBaseAPIResultClassifiesBaseResourcePermission must cover the new
metadata behavior directly: add a log_id to the result fixture and assert it is
exposed through p.LogID, and include an upstream Base error hint in the response
then assert that hint is preserved alongside the generated guidance.
In `@shortcuts/base/base_errors.go`:
- Around line 185-196: Update the permission error construction around
PermissionError.Identity so unresolved cc.Identity remains empty, matching the
ClassifyContext.Identity contract. Keep the "current identity" fallback only in
the hint text, and assign err.Identity from the trimmed resolved identity
without replacing it with display text.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bdc33d91-080a-4a66-85b7-957db54ab43e
📒 Files selected for processing (2)
shortcuts/base/base_errors.goshortcuts/base/base_errors_test.go
| func TestHandleBaseAPIResultClassifiesBaseResourcePermission(t *testing.T) { | ||
| result := map[string]interface{}{ | ||
| "code": 91403, | ||
| "msg": "you don't have permission", | ||
| "data": map[string]interface{}{}, | ||
| } | ||
|
|
||
| _, err := handleBaseAPIResultAny(result, nil, "list dashboards") | ||
| p, ok := errs.ProblemOf(err) | ||
| if !ok { | ||
| t.Fatalf("expected typed error, got %T %v", err, err) | ||
| } | ||
| if p.Code != 91403 { | ||
| t.Fatalf("code=%d", p.Code) | ||
| } | ||
| if p.Category != errs.CategoryAuthorization || p.Subtype != errs.SubtypePermissionDenied { | ||
| t.Fatalf("category/subtype=%s/%s", p.Category, p.Subtype) | ||
| } | ||
| perm, ok := err.(*errs.PermissionError) | ||
| if !ok { | ||
| t.Fatalf("err=%T, want *errs.PermissionError", err) | ||
| } | ||
| if perm.Identity != "current identity" { | ||
| t.Fatalf("identity=%q", perm.Identity) | ||
| } | ||
| for _, want := range []string{"resource access denied", "Base owner", "Do not run auth login"} { | ||
| if !strings.Contains(p.Hint, want) { | ||
| t.Fatalf("hint=%q missing %q", p.Hint, want) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Cover the new metadata branches directly.
Add a log_id fixture and assert p.LogID; also include an upstream Base error hint and assert it is retained. The current test would still pass if either new behavior regressed.
As per coding guidelines, “Every behavior change must have an accompanying test, and contract tests must assert the changed field or behavior directly.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shortcuts/base/base_errors_test.go` around lines 121 - 151, The test
TestHandleBaseAPIResultClassifiesBaseResourcePermission must cover the new
metadata behavior directly: add a log_id to the result fixture and assert it is
exposed through p.LogID, and include an upstream Base error hint in the response
then assert that hint is preserved alongside the generated guidance.
Source: Coding guidelines
| identity := strings.TrimSpace(cc.Identity) | ||
| if identity == "" { | ||
| identity = "current identity" | ||
| } | ||
| hint := fmt.Sprintf("resource access denied for %s; ask the Base owner to grant access, or retry once with --as user only if a user identity is already logged in. Do not run auth login or switch identities repeatedly for this resource-level permission error", identity) | ||
| if upstreamHint != "" { | ||
| hint = upstreamHint + "; " + hint | ||
| } | ||
| err := errs.NewPermissionError(errs.SubtypePermissionDenied, "%s", msg). | ||
| WithCode(91403). | ||
| WithHint("%s", hint) | ||
| err.Identity = identity |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Keep PermissionError.Identity within the classifier contract.
"current identity" is display text, but ClassifyContext.Identity is defined as "user", "bot", or empty. Preserve an empty err.Identity when unresolved; use a separate fallback only in the hint. Otherwise downstream identity-based handling receives an undocumented value.
Proposed fix
- identity := strings.TrimSpace(cc.Identity)
- if identity == "" {
- identity = "current identity"
- }
- hint := fmt.Sprintf("resource access denied for %s; ...", identity)
+ identity := strings.TrimSpace(cc.Identity)
+ displayIdentity := identity
+ if displayIdentity == "" {
+ displayIdentity = "current identity"
+ }
+ hint := fmt.Sprintf("resource access denied for %s; ...", displayIdentity)
...
err.Identity = identity📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| identity := strings.TrimSpace(cc.Identity) | |
| if identity == "" { | |
| identity = "current identity" | |
| } | |
| hint := fmt.Sprintf("resource access denied for %s; ask the Base owner to grant access, or retry once with --as user only if a user identity is already logged in. Do not run auth login or switch identities repeatedly for this resource-level permission error", identity) | |
| if upstreamHint != "" { | |
| hint = upstreamHint + "; " + hint | |
| } | |
| err := errs.NewPermissionError(errs.SubtypePermissionDenied, "%s", msg). | |
| WithCode(91403). | |
| WithHint("%s", hint) | |
| err.Identity = identity | |
| identity := strings.TrimSpace(cc.Identity) | |
| displayIdentity := identity | |
| if displayIdentity == "" { | |
| displayIdentity = "current identity" | |
| } | |
| hint := fmt.Sprintf("resource access denied for %s; ask the Base owner to grant access, or retry once with --as user only if a user identity is already logged in. Do not run auth login or switch identities repeatedly for this resource-level permission error", displayIdentity) | |
| if upstreamHint != "" { | |
| hint = upstreamHint + "; " + hint | |
| } | |
| err := errs.NewPermissionError(errs.SubtypePermissionDenied, "%s", msg). | |
| WithCode(91403). | |
| WithHint("%s", hint) | |
| err.Identity = identity |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@shortcuts/base/base_errors.go` around lines 185 - 196, Update the permission
error construction around PermissionError.Identity so unresolved cc.Identity
remains empty, matching the ClassifyContext.Identity contract. Keep the "current
identity" fallback only in the hint text, and assign err.Identity from the
trimmed resolved identity without replacing it with display text.
🚀 PR Preview Install Guide🧰 CLI updatenpm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@e5ef7ef331f64ab5ff45e44803ad8461172060e4🧩 Skill updatenpx skills add yballul-bytedance/cli#auto-research-sync/01KWYNV56GBPYYSFEHMHM8ZWMP/mr-1031-22c2d3ca -y -g |
Summary
Base API responses with code
91403(resource-level permission denial) were previously flowing through the generic API error path, producing a non-specific error that AI agents could not reliably act on. This change maps91403to a typedPermissionErrorso agents receive a structured, actionable signal instead of a bare API failure.Changes
baseResourcePermissionErrorinshortcuts/base/base_errors.go, which detects the Base91403response code and returns a typederrs.PermissionErrorwithSubtypePermissionDenied.91403), the server log ID when present, and a preserved upstream hint.--as userwhen a user identity is already logged in) rather than repeatedly runningauth loginor switching identities for a resource-level denial.baseAPIErrorFromResultso it runs before the genericerrclass.BuildAPIErrorfallback.TestHandleBaseAPIResultClassifiesBaseResourcePermissioncovering the91403path: asserts category/subtype/code, the*errs.PermissionErrortype, the default identity, and the hint content.Test Plan
git diff --check— passed (no whitespace errors or conflict markers).TestHandleBaseAPIResultClassifiesBaseResourcePermissionasserts the typed metadata (Category,Subtype,Code), the concrete*errs.PermissionErrortype, the defaultIdentity, and the presence of the actionable hint fragments.Related Issues
Auto research task: 01KWYNV56GBPYYSFEHMHM8ZWMP
Summary by CodeRabbit