Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions shortcuts/base/base_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ func baseAPIErrorFromResult(resultMap map[string]interface{}, cc errclass.Classi
if logID := extractBaseErrorLogID(resultMap); logID != "" {
resultMap["log_id"] = logID
}
if err := baseResourcePermissionError(resultMap, cc, hint); err != nil {
return err
}
err := errclass.BuildAPIError(resultMap, cc)
if err == nil {
return nil
Expand All @@ -170,6 +173,33 @@ func baseAPIErrorFromResult(resultMap map[string]interface{}, cc errclass.Classi
return err
}

func baseResourcePermissionError(resultMap map[string]interface{}, cc errclass.ClassifyContext, upstreamHint string) error {
code, ok := util.ToFloat64(resultMap["code"])
if !ok || int(code) != 91403 {
return nil
}
msg, _ := resultMap["msg"].(string)
if strings.TrimSpace(msg) == "" {
msg = "you don't have permission"
}
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
Comment on lines +185 to +196

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ 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.

Suggested change
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.

if logID, _ := resultMap["log_id"].(string); strings.TrimSpace(logID) != "" {
err.WithLogID(strings.TrimSpace(logID))
}
return err
}

func enrichBaseAPIErrorFromBody(err error, body []byte, cc errclass.ClassifyContext) error {
if _, ok := errs.ProblemOf(err); !ok {
return err
Expand Down
32 changes: 32 additions & 0 deletions shortcuts/base/base_errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,38 @@ func TestHandleBaseAPIResultClassifiesKnownPermissionCode(t *testing.T) {
}
}

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)
}
}
}
Comment on lines +121 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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


func TestAttachBaseResponseLogIDFromHeader(t *testing.T) {
result := map[string]interface{}{
"code": 91402,
Expand Down
Loading