Skip to content

fix(auth): preserve concurrently-added credentials on pool rewrite (#19566) - #53896

Merged
teknium1 merged 2 commits into
mainfrom
salvage/19566-credential-pool-stale-rewrite
Jun 28, 2026
Merged

fix(auth): preserve concurrently-added credentials on pool rewrite (#19566)#53896
teknium1 merged 2 commits into
mainfrom
salvage/19566-credential-pool-stale-rewrite

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

Credentials added by hermes auth add now survive a concurrent rotation/exhaustion rewrite by another Hermes process. Previously write_credential_pool blind-overwrote the on-disk pool with the caller's in-memory snapshot, so a long-running gateway that rotated on a 429 would silently erase any credential added since it started.

Root cause: lost-update race. Process A loads the pool → Process B (auth add) appends a credential and writes → Process A marks a key exhausted and persists its stale snapshot, dropping B's addition. No error, no log line.

Salvage of #19608 by @konsisumer, cherry-picked onto current main (his branch was ~2500 commits stale). Fixes #19566.

Changes

  • hermes_cli/auth.py::write_credential_pool: re-reads the on-disk pool inside the existing _auth_store_lock and merges entries present on disk but absent from the caller's snapshot (read-modify-write). New removed_ids kwarg lets callers signal intentional removals so the merge doesn't resurrect them.
  • agent/credential_pool.py: plumbs removed_ids through every prune-then-persist site — the three terminal-auth quarantine paths (xAI loopback_pkce, Codex device_code, Nous singleton), _available_entries expiry prune, remove_index, and the load_pool normalization/prune diff.
  • scripts/release.py: AUTHOR_MAP entry for konsisumer.

Validation

Scenario Before (main) After
A rotates on stale 5-entry snapshot after B added a 6th 6th credential dropped 6th preserved + A's exhausted marker applied
remove_index then later write (n/a) removed entry stays gone, not resurrected by merge
  • E2E against real imports + temp HERMES_HOME: reproduced the exact field scenario (brendatori dropped on old blind-overwrite, preserved with the fix); confirmed remove_index not undone by merge.
  • tests/agent/test_credential_pool.py: 81 passed (includes the 2 new regression tests test_persist_preserves_concurrent_disk_only_entry + test_remove_index_does_not_resurrect_via_disk_merge).
  • The new regression test fails on main and passes here.

Two community reporters independently field-verified #19608 against origin/main (entry dropped on main, preserved on the branch).

Note: #19566 was previously auto-closed as resolved by #33744 — that PR fixes a different bug (device-code token-content sync on re-auth, a 401 loop) and never touches this write race.

Infographic

credential-pool-no-lost-writes

@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: salvage/19566-credential-pool-stale-rewrite vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11521 on HEAD, 11519 on base (🆕 +2)

🆕 New issues (2):

Rule Count
invalid-argument-type 1
unresolved-attribute 1
First entries
tests/agent/test_credential_pool.py:3097: [invalid-argument-type] invalid-argument-type: Argument to function `write_credential_pool` is incorrect: Expected `list[dict[str, Any]]`, found `dict[str, Any]`
tests/agent/test_credential_pool.py:3087: [unresolved-attribute] unresolved-attribute: Object of type `dict[str, Any]` has no attribute `append`

✅ Fixed issues: none

Unchanged: 6064 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1
teknium1 merged commit ea8face into main Jun 28, 2026
30 checks passed
@teknium1
teknium1 deleted the salvage/19566-credential-pool-stale-rewrite branch June 28, 2026 02:01
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard area/auth Authentication, OAuth, credential pools sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Jun 28, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Salvage of #19608 (@konsisumer), cherry-picked onto current main. Fixes the lost-update race in #19566. Related: #19566 (issue), #19608, #7202. Not a duplicate — this is the reworked version that re-reads on disk inside the lock and merges, with a removed_ids kwarg so intentional removals aren't resurrected.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

Thorough fix for credential pool concurrent-write race condition (#19566). The core issue: between loading the in-memory credential snapshot and persisting it, another process may add a credential — the later persist drops the concurrent addition.

The fix:

  • write_credential_pool now re-reads the on-disk pool under the same lock and merges entries present on disk but missing from the caller's entries
  • removed_ids parameter prevents the merge from resurrecting intentionally-removed credentials
  • Both load_pool and _persist track and propagate removed IDs
  • Multiple call sites updated (3 terminal auth failure paths + expiry pruning + load_pool normalization)

Well-documented with clear rationale for the merge pattern. No concerns. Ready to merge.


Reviewed by Hermes Agent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard P1 High — major feature broken, no workaround sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: OpenAI-Codex credential pool can drop newly added credential after stale auth.json rewrite during rotation

4 participants