Skip to content

fix: a fallback whose first identity is being saved is still admitted - #420

Merged
ualtinok merged 1 commit into
mainfrom
fix/first-identity-admission
Oct 21, 2026
Merged

ualtinok merged 1 commit into
mainfrom
fix/first-identity-admission

Conversation

@antauth-alfonso

@antauth-alfonso antauth-alfonso Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Found by the 2.2.0 release PR's CI. A request could skip a fallback account whose identity was being saved for the first time and return the main account's error instead: the request had captured the account's binding before the identity existed, and authorization then found the same row with the identity set (or caught a partly written state/config pair) and refused it as row-ineligible. 2 of 40 runs of the affected test on main, 0 of 40 on v2.1.2; the change in cause-keyed refresh joining (#414) stopped serve requests from waiting on that publication.

  • A serve request that finds its row mid-write (same credential epoch) waits once for that writer with the existing torn-row barrier and budget, then rereads; still torn returns the existing retryable account error.
  • If the row now carries the first identity and the store, row and credential epoch still match, the request adopts that identity. Admission still requires its normal permission and exact proof; custody, quota, refresh-error and rejected-token exclusions are unchanged.

Red-first Core regressions for both windows; mutation rows serve-adopts-concurrent-first-identity and serve-joins-concurrent-first-identity-write CAUGHT. After the fix: 0 status-400 misses in 40 + 40 Linux runs. No changelog line: the bug isn't in any release.


Summary by cubic

Fixes a race that let a serve request skip a fallback account whose first identity was being saved, returning the main provider's 400 instead.

  • In-flight first-identity writes on the same credential epoch are now joined via the existing torn-row barrier and budget before the row is judged ineligible.
  • A request adopting a newly published first identity only proceeds if store, row, and credential epoch still match; admission permissions, proof checks, and all existing exclusions (custody, quota, refresh-error, rejected-token) are unchanged.
  • Added two mutation controls with red-first regressions, plus tests covering adoption, torn-write join, transient-error branding, and preserved admission exclusions.
  • Not in a release, so no changelog line.

Written for commit a6efa38. Summary will update on new commits.

View guided diff Turn on auto-fix

A profile-triggered recovery can publish a fallback's first UUID between the host snapshot and the coordinator's pre-join read. Exact binding admission then dropped the fallback as row-ineligible, returning the main provider 400. A second window exposes a torn state/config row at that same read.

Join recoverable same-epoch pool writes with the existing bounded budget, then adopt only a first identity whose store, row and epoch still match. Keep all admission exclusions and exact-version proof checks, and preserve the branded transient error when the writer cannot finish.

Both race windows have red-first Core regressions and CAUGHT named mutation controls with Core broad settings. Existing history tests are unchanged. Pinned Bun 1.3.14 Linux loops: main originally had 2/40 single and 3/40 block status-400 misses; v2.1.2 had none; fixed loops have none in 40+40 processes, with one unrelated default-deadline timeout in each. Core owning file: 122 pass. Final OpenCode owning file: 304 pass, two claude-prime timeouts on the shared runner. Core/OpenCode typechecks, scripts typecheck, lint and formatting pass.
@ualtinok
ualtinok merged commit a6efa38 into main Oct 21, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant