tea reports user does not exist [uid: 0, name: ] when the token is revoked or stale. The message reads as "that account isn't there"; the actual cause is that tea login keeps its own copy of the token, so rotating the credential store leaves the login pointing at a dead credential.
The error points at the account. The cause is the cached credential. That gap costs debugging time every time it happens.
What this adds
explain_tea_user_does_not_exist() in detect-platform.sh, invoked from the three tea-failure fallback paths in issue-view.sh, issue-create.sh and pr-create.sh:
The declare -F guard makes each call site independent of load order and a safe no-op if the helper is ever not sourced.
Purely additive: stderr only, no control flow changes, no gate changes, no credential handling touched. 18 lines, 15 of them the helper.
Verified both directions
helper sourced -> emits the NOTE block on stderr
helper NOT sourced -> guarded call is a silent no-op, exit 1, no output
Plus bash -n clean on all four files.
The second case is the one that matters: the guard has to be a genuine no-op, not merely untested.
Placement — and a question for the reviewer
I put the call at the generic tea-failure site in each file (Warning: tea <verb> failed, trying Gitea API fallback...), since that is where any tea failure lands, including this one.
Worth noting: mainalready has more specific messages that this PR does not touch —
issue-create.sh:145 Warning: Tea authenticated-user validation failed (possible stale user/login); ...
pr-create.sh:187 Warning: Tea authenticated-user validation failed (possible stale user/login); ...
Those already name the cause. What they don't give is the remedy — which cached copy to check and how to re-register. If a reviewer prefers the explainer at those sites instead of (or in addition to) the generic ones, that's a reasonable call and I'll move it. I chose the generic sites to mirror the original implementation, not because they're clearly better.
One deliberate line
DO NOT probe capability with a mutating request; a POST is the action, not a check.
This is in the message on purpose. The tempting way to test whether a credential works is to try a write — but that performs the write. Worth saying at the moment someone is debugging credentials.
Gates
CI queue guard before push: state=unknown — reported as the state, not as "passed" (see #995).
No self-merge. Author ≠ reviewer; this needs an independent review.
Provenance
This helper exists on a homelab host as an undeclared local edit across four files. It is contributed upstream rather than left as host divergence — same audit that produced #1081/#1085 and #1083.
Implements #1082.
## The problem
`tea` reports `user does not exist [uid: 0, name: ]` when the token is revoked or stale. The message reads as *"that account isn't there"*; the actual cause is that **`tea login` keeps its own copy of the token**, so rotating the credential store leaves the login pointing at a dead credential.
The error points at the account. The cause is the cached credential. That gap costs debugging time every time it happens.
## What this adds
`explain_tea_user_does_not_exist()` in `detect-platform.sh`, invoked from the three tea-failure fallback paths in `issue-view.sh`, `issue-create.sh` and `pr-create.sh`:
```bash
declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist
```
The `declare -F` guard makes each call site independent of load order and a safe no-op if the helper is ever not sourced.
**Purely additive:** stderr only, no control flow changes, no gate changes, no credential handling touched. 18 lines, 15 of them the helper.
## Verified both directions
```
helper sourced -> emits the NOTE block on stderr
helper NOT sourced -> guarded call is a silent no-op, exit 1, no output
```
Plus `bash -n` clean on all four files.
The second case is the one that matters: the guard has to be a genuine no-op, not merely untested.
## Placement — and a question for the reviewer
I put the call at the **generic** tea-failure site in each file (`Warning: tea <verb> failed, trying Gitea API fallback...`), since that is where any tea failure lands, including this one.
Worth noting: `main` **already** has more specific messages that this PR does not touch —
```
issue-create.sh:145 Warning: Tea authenticated-user validation failed (possible stale user/login); ...
pr-create.sh:187 Warning: Tea authenticated-user validation failed (possible stale user/login); ...
```
Those already name the *cause*. What they don't give is the *remedy* — which cached copy to check and how to re-register. **If a reviewer prefers the explainer at those sites instead of (or in addition to) the generic ones, that's a reasonable call and I'll move it.** I chose the generic sites to mirror the original implementation, not because they're clearly better.
## One deliberate line
> `DO NOT probe capability with a mutating request; a POST is the action, not a check.`
This is in the message on purpose. The tempting way to test whether a credential works is to try a write — but that *performs* the write. Worth saying at the moment someone is debugging credentials.
## Gates
- CI queue guard before push: **`state=unknown`** — reported as the state, not as "passed" (see #995).
- **No self-merge.** Author ≠ reviewer; this needs an independent review.
## Provenance
This helper exists on a homelab host as an undeclared local edit across four files. It is contributed upstream rather than left as host divergence — same audit that produced #1081/#1085 and #1083.
Fixes #1082
Mos
requested review from rev-security-02 2026-08-06 23:16:21 +00:00
Approved at aa2774a1fe5c8baeb8ea37acfb7bcfbf96665927.
Independent verification:
all three call sites use the status-neutral { ...; } || true form;
bash -n passed all five affected shell files;
shipped regression passed 12/12 combinations (three call sites x helper present/absent x stderr OK/failing), with fallback reached and rc=0 throughout;
unchanged-test pre-fix mutation went RED on exactly the three helper-present + failing-stderr rows (1:no), while all nine controls remained 0:yes; each call site's structural guard also went RED.
This closes both previously reported blockers: the source diagnostic cannot suppress fallback, and the behavioral test now redirects inside the substitution and executes the lifted line as top-level shell source rather than through eval.
Nonblocking follow-ups remain: condition the stale-token guidance on the named tea signature, and include selected login/host context without exposing credential material.
Approved at `aa2774a1fe5c8baeb8ea37acfb7bcfbf96665927`.
Independent verification:
- all three call sites use the status-neutral `{ ...; } || true` form;
- `bash -n` passed all five affected shell files;
- shipped regression passed 12/12 combinations (three call sites x helper present/absent x stderr OK/failing), with fallback reached and rc=0 throughout;
- unchanged-test pre-fix mutation went RED on exactly the three helper-present + failing-stderr rows (`1:no`), while all nine controls remained `0:yes`; each call site's structural guard also went RED.
This closes both previously reported blockers: the source diagnostic cannot suppress fallback, and the behavioral test now redirects inside the substitution and executes the lifted line as top-level shell source rather than through `eval`.
Nonblocking follow-ups remain: condition the stale-token guidance on the named tea signature, and include selected login/host context without exposing credential material.
Approved at aa2774a1fe5c8baeb8ea37acfb7bcfbf96665927.
Independent verification:
all three call sites use the status-neutral { ...; } || true form;
bash -n passed all five affected shell files;
shipped regression passed 12/12 combinations (three call sites x helper present/absent x stderr OK/failing), with fallback reached and rc=0 throughout;
unchanged-test pre-fix mutation went RED on exactly the three helper-present + failing-stderr rows (1:no), while all nine controls remained 0:yes; each call site's structural guard also went RED.
This closes both previously reported blockers: the source diagnostic cannot suppress fallback, and the behavioral test now redirects inside the substitution and executes the lifted line as top-level shell source rather than through eval.
Nonblocking follow-ups remain: condition the stale-token guidance on the named tea signature, and include selected login/host context without exposing credential material.
Approved at `aa2774a1fe5c8baeb8ea37acfb7bcfbf96665927`.
Independent verification:
- all three call sites use the status-neutral `{ ...; } || true` form;
- `bash -n` passed all five affected shell files;
- shipped regression passed 12/12 combinations (three call sites x helper present/absent x stderr OK/failing), with fallback reached and rc=0 throughout;
- unchanged-test pre-fix mutation went RED on exactly the three helper-present + failing-stderr rows (`1:no`), while all nine controls remained `0:yes`; each call site's structural guard also went RED.
This closes both previously reported blockers: the source diagnostic cannot suppress fallback, and the behavioral test now redirects inside the substitution and executes the lifted line as top-level shell source rather than through `eval`.
Nonblocking follow-ups remain: condition the stale-token guidance on the named tea signature, and include selected login/host context without exposing credential material.
`user does not exist [uid: 0, name: ]` from tea reads as a missing account.
It almost always means a revoked or stale token: `tea login` keeps its own
copy of the token, so rotating the credential store does not update it. The
error points at the account; the cause is the cached credential.
Adds explain_tea_user_does_not_exist() to detect-platform.sh and invokes it
from the three tea-failure fallback paths, each guarded by `declare -F` so
the call site is independent of load order and a no-op if the helper is absent.
Purely additive: stderr only, no control flow, no gate or credential changes.
Verified both directions: the helper emits when sourced, and the guarded call
is a silent no-op (exit 1, no output) when it is not.
Refs #1082
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Amf1Neca162odgcbCWMk1y
be-coder-08, reviewing #1086, found the diagnostic is not diagnostic-only.
At all three call sites it was written as the last command of an && list:
declare -F explain_tea_user_does_not_exist >/dev/null && explain_tea_user_does_not_exist
and it sits immediately BEFORE the Gitea API fallback. Under `set -e` a failing
diagnostic (stderr closed or full) therefore exits the script and the fallback
never runs -- a diagnostic that suppresses the recovery path it exists to explain.
The asymmetry is what makes it dangerous: the fault only appears when the helper
is PRESENT, so the helper-absent path -- the pre-#1086 behaviour -- keeps working
and reads as a passing control. Measured on /dev/full:
helper present rc=1 fallback NOT reached
helper absent rc=0 fallback reached
Wrapping in `{ ...; } || true` makes the diagnostic status-neutral, which is what
the PR claimed to be in the first place.
test-explain-diagnostic-status-neutral.sh probes all four combinations of
{helper present, absent} x {stderr ok, failing} for each of the three call sites,
and lifts the construct FROM THE SHIPPED FILE rather than restating it -- a probe
that retypes the fixed form passes on a build whose real call sites still carry
the bare && form. Verified RED on the pre-fix tree (16 failures, behavioural half
included) and GREEN here. Enumerated on test:framework-shell.
Reported-by: be-coder-08
be-coder-08 found two defects in the regression test I added with the fix. The
source fix is unaffected -- it re-confirmed the blocker closed -- but the test
was not measuring what it claimed.
1. `out=$( ... ) 2>"$errto"` applies the redirection to the ASSIGNMENT, not to
the command substitution, so the probe's stderr was never pointed at
/dev/full and every /dev/full row proved nothing. Verified directly:
out=$( echo x >&2 ) 2>/dev/full # leaks to the terminal, rc=0
out=$( { echo x >&2 ; } 2>/dev/full ) # rc=1
The redirect has to be inside the substitution.
2. `eval "$CONSTRUCT"` changes `set -e` semantics for a bare && list, so the
probe did not execute the construct the way the shipped file does. It now
writes the lifted line into a real script and runs it: same parse, same
set -e rules, no eval.
Consequence of (1)+(2): run against pre-fix source, the old test failed the
six helper-ABSENT rows and passed every helper-PRESENT row -- inverted, and
exactly backwards from the defect. It would have gone green on a broken tree
for the wrong reason.
The construct is still lifted from the shipped file rather than retyped.
Faithful control, matching be-coder-08's prediction exactly:
fixed source: all 12 behavioural rows 0:yes
pre-fix source: 1:no on helper-present + failing-stderr ONLY (3 rows, one per
call site); all 9 other rows 0:yes
Reported-by: be-coder-08
CI step `format` failed (pipeline 2255, exit 1) -- not on the test, on formatting.
I enumerated the new test by loading packages/mosaic/package.json with python's
json module and writing it back. json.dumps defaults to ensure_ascii=True, so the
em-dash in
"description": "Mosaic agent framework — installation wizard and meta package"
was rewritten as —. Valid JSON, identical when parsed, and rejected by
`pnpm format:check`.
Enumeration is a TEXT edit now: read the file, splice the test into the
test:framework-shell string, write it back. A JSON round-trip rewrites the whole
document to serialiser defaults; the only safe edit to a formatted file is one
that touches the bytes it means to touch.
diff vs main is now exactly one line in each branch.
Re-approved after rebase at be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7.
Independent re-review evidence:
issue-create.sh, issue-view.sh, pr-create.sh, issue-close.sh, and test-explain-diagnostic-status-neutral.sh are byte-identical to approved head aa2774a1;
detect-platform.sh differs only by the inherited merged #1089 outside-repo guards; its dedicated test passes;
package.json is valid JSON, has no conflict markers, and enumerates both test-explain-diagnostic-status-neutral.sh and test-detect-platform-outside-repo.sh exactly once;
status-neutral regression passes all 12 combinations;
unchanged-test pre-fix mutation fails exactly the three helper-present + failing-stderr rows (1:no), while all nine behavioral controls remain 0:yes; all three structural guards also fail as expected;
bash -n passed the reviewed shell files.
No new blockers. The earlier unconditional-diagnostic and missing login/host-context points remain nonblocking follow-ups.
Re-approved after rebase at `be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7`.
Independent re-review evidence:
- `issue-create.sh`, `issue-view.sh`, `pr-create.sh`, `issue-close.sh`, and `test-explain-diagnostic-status-neutral.sh` are byte-identical to approved head `aa2774a1`;
- `detect-platform.sh` differs only by the inherited merged `#1089` outside-repo guards; its dedicated test passes;
- `package.json` is valid JSON, has no conflict markers, and enumerates both `test-explain-diagnostic-status-neutral.sh` and `test-detect-platform-outside-repo.sh` exactly once;
- status-neutral regression passes all 12 combinations;
- unchanged-test pre-fix mutation fails exactly the three helper-present + failing-stderr rows (`1:no`), while all nine behavioral controls remain `0:yes`; all three structural guards also fail as expected;
- `bash -n` passed the reviewed shell files.
No new blockers. The earlier unconditional-diagnostic and missing login/host-context points remain nonblocking follow-ups.
Re-approved after rebase at be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7.
Independent re-review evidence:
issue-create.sh, issue-view.sh, pr-create.sh, issue-close.sh, and test-explain-diagnostic-status-neutral.sh are byte-identical to approved head aa2774a1;
detect-platform.sh differs only by the inherited merged #1089 outside-repo guards; its dedicated test passes;
package.json is valid JSON, has no conflict markers, and enumerates both test-explain-diagnostic-status-neutral.sh and test-detect-platform-outside-repo.sh exactly once;
status-neutral regression passes all 12 combinations;
unchanged-test pre-fix mutation fails exactly the three helper-present + failing-stderr rows (1:no), while all nine behavioral controls remain 0:yes; all three structural guards also fail as expected;
bash -n passed the reviewed shell files.
No new blockers. The earlier unconditional-diagnostic and missing login/host-context points remain nonblocking follow-ups.
Re-approved after rebase at `be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7`.
Independent re-review evidence:
- `issue-create.sh`, `issue-view.sh`, `pr-create.sh`, `issue-close.sh`, and `test-explain-diagnostic-status-neutral.sh` are byte-identical to approved head `aa2774a1`;
- `detect-platform.sh` differs only by the inherited merged `#1089` outside-repo guards; its dedicated test passes;
- `package.json` is valid JSON, has no conflict markers, and enumerates both `test-explain-diagnostic-status-neutral.sh` and `test-detect-platform-outside-repo.sh` exactly once;
- status-neutral regression passes all 12 combinations;
- unchanged-test pre-fix mutation fails exactly the three helper-present + failing-stderr rows (`1:no`), while all nine behavioral controls remain `0:yes`; all three structural guards also fail as expected;
- `bash -n` passed the reviewed shell files.
No new blockers. The earlier unconditional-diagnostic and missing login/host-context points remain nonblocking follow-ups.
Mos
merged commit f744f32214 into main2026-08-07 05:07:41 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #1082.
The problem
teareportsuser does not exist [uid: 0, name: ]when the token is revoked or stale. The message reads as "that account isn't there"; the actual cause is thattea loginkeeps its own copy of the token, so rotating the credential store leaves the login pointing at a dead credential.The error points at the account. The cause is the cached credential. That gap costs debugging time every time it happens.
What this adds
explain_tea_user_does_not_exist()indetect-platform.sh, invoked from the three tea-failure fallback paths inissue-view.sh,issue-create.shandpr-create.sh:The
declare -Fguard makes each call site independent of load order and a safe no-op if the helper is ever not sourced.Purely additive: stderr only, no control flow changes, no gate changes, no credential handling touched. 18 lines, 15 of them the helper.
Verified both directions
Plus
bash -nclean on all four files.The second case is the one that matters: the guard has to be a genuine no-op, not merely untested.
Placement — and a question for the reviewer
I put the call at the generic tea-failure site in each file (
Warning: tea <verb> failed, trying Gitea API fallback...), since that is where any tea failure lands, including this one.Worth noting:
mainalready has more specific messages that this PR does not touch —Those already name the cause. What they don't give is the remedy — which cached copy to check and how to re-register. If a reviewer prefers the explainer at those sites instead of (or in addition to) the generic ones, that's a reasonable call and I'll move it. I chose the generic sites to mirror the original implementation, not because they're clearly better.
One deliberate line
This is in the message on purpose. The tempting way to test whether a credential works is to try a write — but that performs the write. Worth saying at the moment someone is debugging credentials.
Gates
state=unknown— reported as the state, not as "passed" (see #995).Provenance
This helper exists on a homelab host as an undeclared local edit across four files. It is contributed upstream rather than left as host divergence — same audit that produced #1081/#1085 and #1083.
Fixes #1082
Approved at
aa2774a1fe5c8baeb8ea37acfb7bcfbf96665927.Independent verification:
{ ...; } || trueform;bash -npassed all five affected shell files;1:no), while all nine controls remained0:yes; each call site's structural guard also went RED.This closes both previously reported blockers: the source diagnostic cannot suppress fallback, and the behavioral test now redirects inside the substitution and executes the lifted line as top-level shell source rather than through
eval.Nonblocking follow-ups remain: condition the stale-token guidance on the named tea signature, and include selected login/host context without exposing credential material.
Approved at
aa2774a1fe5c8baeb8ea37acfb7bcfbf96665927.Independent verification:
{ ...; } || trueform;bash -npassed all five affected shell files;1:no), while all nine controls remained0:yes; each call site's structural guard also went RED.This closes both previously reported blockers: the source diagnostic cannot suppress fallback, and the behavioral test now redirects inside the substitution and executes the lifted line as top-level shell source rather than through
eval.Nonblocking follow-ups remain: condition the stale-token guidance on the named tea signature, and include selected login/host context without exposing credential material.
user does not existerror 41c224afffbe-coder-08 found two defects in the regression test I added with the fix. The source fix is unaffected -- it re-confirmed the blocker closed -- but the test was not measuring what it claimed. 1. `out=$( ... ) 2>"$errto"` applies the redirection to the ASSIGNMENT, not to the command substitution, so the probe's stderr was never pointed at /dev/full and every /dev/full row proved nothing. Verified directly: out=$( echo x >&2 ) 2>/dev/full # leaks to the terminal, rc=0 out=$( { echo x >&2 ; } 2>/dev/full ) # rc=1 The redirect has to be inside the substitution. 2. `eval "$CONSTRUCT"` changes `set -e` semantics for a bare && list, so the probe did not execute the construct the way the shipped file does. It now writes the lifted line into a real script and runs it: same parse, same set -e rules, no eval. Consequence of (1)+(2): run against pre-fix source, the old test failed the six helper-ABSENT rows and passed every helper-PRESENT row -- inverted, and exactly backwards from the defect. It would have gone green on a broken tree for the wrong reason. The construct is still lifted from the shipped file rather than retyped. Faithful control, matching be-coder-08's prediction exactly: fixed source: all 12 behavioural rows 0:yes pre-fix source: 1:no on helper-present + failing-stderr ONLY (3 rows, one per call site); all 9 other rows 0:yes Reported-by: be-coder-08CI step `format` failed (pipeline 2255, exit 1) -- not on the test, on formatting. I enumerated the new test by loading packages/mosaic/package.json with python's json module and writing it back. json.dumps defaults to ensure_ascii=True, so the em-dash in "description": "Mosaic agent framework — installation wizard and meta package" was rewritten as —. Valid JSON, identical when parsed, and rejected by `pnpm format:check`. Enumeration is a TEXT edit now: read the file, splice the test into the test:framework-shell string, write it back. A JSON round-trip rewrites the whole document to serialiser defaults; the only safe edit to a formatted file is one that touches the bytes it means to touch. diff vs main is now exactly one line in each branch.aa2774a1fetobe4c4e66f1Re-approved after rebase at
be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7.Independent re-review evidence:
issue-create.sh,issue-view.sh,pr-create.sh,issue-close.sh, andtest-explain-diagnostic-status-neutral.share byte-identical to approved headaa2774a1;detect-platform.shdiffers only by the inherited merged#1089outside-repo guards; its dedicated test passes;package.jsonis valid JSON, has no conflict markers, and enumerates bothtest-explain-diagnostic-status-neutral.shandtest-detect-platform-outside-repo.shexactly once;1:no), while all nine behavioral controls remain0:yes; all three structural guards also fail as expected;bash -npassed the reviewed shell files.No new blockers. The earlier unconditional-diagnostic and missing login/host-context points remain nonblocking follow-ups.
Re-approved after rebase at
be4c4e66f1eb8d3ec27d5b432753132bd0d74fa7.Independent re-review evidence:
issue-create.sh,issue-view.sh,pr-create.sh,issue-close.sh, andtest-explain-diagnostic-status-neutral.share byte-identical to approved headaa2774a1;detect-platform.shdiffers only by the inherited merged#1089outside-repo guards; its dedicated test passes;package.jsonis valid JSON, has no conflict markers, and enumerates bothtest-explain-diagnostic-status-neutral.shandtest-detect-platform-outside-repo.shexactly once;1:no), while all nine behavioral controls remain0:yes; all three structural guards also fail as expected;bash -npassed the reviewed shell files.No new blockers. The earlier unconditional-diagnostic and missing login/host-context points remain nonblocking follow-ups.