What happened?
gl list/show commands ignore the HTTP status code and map any denial body (403/404) to an empty list with exit 0. A caller who may not read a repo (or hits a withheld subtree / revoked access / dead node) sees “No …” / 0/0/0 / stub ? output instead of the denial. This turns an authorization boundary into invisible data loss.
Per AGENTS.md > Client behavior: “When a node denies a request, surface the denial to the user. Never render a denial as an empty list or a silent success.”
Affected paths (all Ok(()), no status.is_success() check before .json() + unwrap_or_default()):
crates/gl/src/status.rs:84-86 (prs = body["pulls"]…unwrap_or_default() → “no open pull requests”), 111-113 (issues, same)
crates/gl/src/issue.rs:224-231 cmd_list → “No issues for {owner}/{name}”; 347-365 cmd_issue_comments → “No comments on issue {id}”
crates/gl/src/pr.rs:252-264 cmd_list → “No pull requests”; 296-321 cmd_view → stub card ? (title/status unwrap_or("?") on error body); 324-332 reviews / 357-365 comments silently omitted; 384-398 cmd_diff → “No diff (branches may be identical…)”
crates/gl/src/bounty.rs:234-255 cmd_list → silent Ok(()), prints nothing (body["bounties"] is None on 403); 384-396 cmd_stats → open: 0 / claimed: 0 / completed: 0
crates/gl/src/task.rs:169-276 all commands print_json(&resp) without status check, so {"message":"…"} prints as if data (self-documented in tests at task.rs:361,451)
crates/gl/src/cert.rs:103-110 cmd_list → “No ref certificates”
crates/gl/src/repo.rs:280-286 cmd_list (403 body fails as_array context as opaque “expected array”, hides 403/404), 524-531 commits → “No commits”
crates/gl/src/peer.rs:66-73 cmd_list → “No known peers”; 259-264 ping / 284-289 resolve → ?/unreachable with Ok
crates/gl/src/node.rs:177-183 try_get_json drops non-2xx to None → “unavailable”, always Ok
crates/gl/src/clone.rs:320-326 recover_encrypted_blobs: any non-2xx → Ok(vec![]), caller prints Done. Cloned… / Recovered N files short with no warning
crates/gl/src/whoami.rs:52-56 nested repos fetch ignores status, silently omits count
Contrast: sibling commands in the same files do it correctly and prove the intended pattern — e.g. issue.rs:270 cmd_show, bounty.rs:265 cmd_show / 295 cmd_claim, cert.rs:147 cmd_show (.error_for_status()), repo.rs:354 cmd_info / 682 cmd_label_list all bail!("… ({status}): {msg}").
Expected behavior
On non-2xx, gl should surface the denial and exit non-zero, e.g. bail!("list failed ({status}): {msg}") using the already-established pattern from cmd_show/cmd_claim, never unwrap_or_default() to empty. gl status should print e.g. PRs ✗ denied (403) rather than no open pull requests.
Steps to reproduce
mockito (or any stub) returning 403 {"message":"forbidden"} for a list route, e.g.:
GET /api/v1/repos/{owner}/{repo}/issues → 403
gl issue list --repo myrepo --node $MOCK → prints No issues for …, exit 0 (see issue.rs:231-235; same shape for gl pr list, gl bounty list, gl cert list, gl peer list).
GET /api/v1/bounties/stats → 403 + gl bounty stats → prints open: 0 / claimed: 0 / completed: 0, exit 0.
- Revoke read on a private repo (or stop node) +
gl clone with encrypted blobs → Ok(vec![]) at clone.rs:325, missing files reported as success.
Affected crate
gl
Version or commit
bfc44f92 (main, upstream/main @ 2026-09-05)
Environment
Linux, rustc 1.97.1, workspace gl CLI against any node returning non-2xx.
What happened?
gllist/show commands ignore the HTTP status code and map any denial body (403/404) to an empty list with exit0. A caller who may not read a repo (or hits a withheld subtree / revoked access / dead node) sees “No …” /0/0/0/ stub?output instead of the denial. This turns an authorization boundary into invisible data loss.Per
AGENTS.md> Client behavior: “When a node denies a request, surface the denial to the user. Never render a denial as an empty list or a silent success.”Affected paths (all
Ok(()), nostatus.is_success()check before.json()+unwrap_or_default()):crates/gl/src/status.rs:84-86(prs = body["pulls"]…unwrap_or_default()→ “no open pull requests”),111-113(issues, same)crates/gl/src/issue.rs:224-231cmd_list→ “No issues for {owner}/{name}”;347-365cmd_issue_comments→ “No comments on issue {id}”crates/gl/src/pr.rs:252-264cmd_list→ “No pull requests”;296-321cmd_view→ stub card?(title/statusunwrap_or("?")on error body);324-332reviews /357-365comments silently omitted;384-398cmd_diff→ “No diff (branches may be identical…)”crates/gl/src/bounty.rs:234-255cmd_list→ silentOk(()), prints nothing (body["bounties"]isNoneon 403);384-396cmd_stats→open: 0 / claimed: 0 / completed: 0crates/gl/src/task.rs:169-276all commandsprint_json(&resp)without status check, so{"message":"…"}prints as if data (self-documented in tests attask.rs:361,451)crates/gl/src/cert.rs:103-110cmd_list→ “No ref certificates”crates/gl/src/repo.rs:280-286cmd_list(403 body failsas_arraycontext as opaque “expected array”, hides 403/404),524-531commits → “No commits”crates/gl/src/peer.rs:66-73cmd_list→ “No known peers”;259-264ping /284-289resolve →?/unreachablewithOkcrates/gl/src/node.rs:177-183try_get_jsondrops non-2xx toNone→ “unavailable”, alwaysOkcrates/gl/src/clone.rs:320-326recover_encrypted_blobs: any non-2xx →Ok(vec![]), caller printsDone. Cloned… / Recovered N filesshort with no warningcrates/gl/src/whoami.rs:52-56nested repos fetch ignores status, silently omits countContrast: sibling commands in the same files do it correctly and prove the intended pattern — e.g.
issue.rs:270 cmd_show,bounty.rs:265 cmd_show / 295 cmd_claim,cert.rs:147 cmd_show (.error_for_status()),repo.rs:354 cmd_info / 682 cmd_label_listallbail!("… ({status}): {msg}").Expected behavior
On non-2xx,
glshould surface the denial and exit non-zero, e.g.bail!("list failed ({status}): {msg}")using the already-established pattern fromcmd_show/cmd_claim, neverunwrap_or_default()to empty.gl statusshould print e.g.PRs ✗ denied (403)rather thanno open pull requests.Steps to reproduce
mockito(or any stub) returning403 {"message":"forbidden"}for a list route, e.g.:GET /api/v1/repos/{owner}/{repo}/issues → 403gl issue list --repo myrepo --node $MOCK→ printsNo issues for …, exit0(seeissue.rs:231-235; same shape forgl pr list,gl bounty list,gl cert list,gl peer list).GET /api/v1/bounties/stats → 403+gl bounty stats→ printsopen: 0 / claimed: 0 / completed: 0, exit0.gl clonewith encrypted blobs →Ok(vec![])atclone.rs:325, missing files reported as success.Affected crate
gl
Version or commit
bfc44f92(main,upstream/main@ 2026-09-05)Environment
Linux,
rustc 1.97.1, workspaceglCLI against any node returning non-2xx.