Skip to content

fix(auth-profiles): make post-success bookkeeping saves non-fatal#67064

Closed
ademczuk wants to merge 1 commit into
openclaw:mainfrom
ademczuk:fix-auth-profiles-eperm-non-fatal
Closed

fix(auth-profiles): make post-success bookkeeping saves non-fatal#67064
ademczuk wants to merge 1 commit into
openclaw:mainfrom
ademczuk:fix-auth-profiles-eperm-non-fatal

Conversation

@ademczuk

Copy link
Copy Markdown
Contributor

Summary

Fixes #62099. On Windows, concurrent config hot-reload can leave auth-profiles.json with a ReadOnly attribute. The atomic write in saveAuthProfileStore then throws EPERM, and because markAuthProfileGood / markAuthProfileUsed / markAuthProfileFailure run as post-completion bookkeeping, that throw used to cascade into the LLM request that had already succeeded. Fallback triggers, hits the same read-only file, fails the same way. The gateway becomes unresponsive; restarts don't help because the file attribute persists.

The fix wraps the body of each mark* function in try/catch, logging the persistence failure and continuing. Caller-visible behavior is unchanged on the happy path.

What this does NOT change

saveAuthProfileStore itself still throws on failure. OAuth token refresh (in oauth.ts) depends on that behavior, since a silent token-save failure would be a security concern. Only the three mark* functions that run after a successful provider call now tolerate save errors.

Scope

  • src/agents/auth-profiles/profiles.ts - wrap markAuthProfileGood body in try/catch, log warn
  • src/agents/auth-profiles/usage.ts - wrap markAuthProfileUsed and markAuthProfileFailure bodies in try/catch, log warn
  • src/agents/auth-profiles/usage.persist-nonfatal.test.ts - new regression tests, one per mark function, simulating EPERM from both the lock-guarded and direct save paths

markAuthProfileCooldown delegates to markAuthProfileFailure so it's covered transitively.

Before

Error: EPERM: operation not permitted, copyfile
  'auth-profiles.json.<uuid>.tmp' -> 'auth-profiles.json'
    at Object.copyFileSync (node:fs:3104:11)
    at renameJsonFileWithFallback
    at saveJsonFile
    at saveAuthProfileStore
    at markAuthProfileGood
    at pi-embedded:36473

The LLM response arrived, then the save threw, then every fallback hit the same file, then the gateway ran out of models. User hits attrib -R on the file and restarts to recover.

After

The save throws, the catch runs, the warning lands in the subsystem log, the mark function returns void, the LLM request completes normally. lastGood / usage stats stay slightly stale until the next successful save, which is the right tradeoff for a bookkeeping write.

Testing

  • New tests in usage.persist-nonfatal.test.ts pass (3/3). Mocks both updateAuthProfileStoreWithLock and saveAuthProfileStore to throw EPERM, asserts the mark* functions resolve without throwing.
  • Existing usage.test.ts (39 tests) and auth-profiles.markauthprofilefailure.test.ts (9 tests) still pass. Also verified auth-profiles.runtime-snapshot-save.test.ts (1 test).
  • Two pre-existing test failures in state-observation.test.ts and oauth.fallback-to-main-agent.test.ts already fail on main, unrelated to this change.
  • oxlint --type-aware clean on modified files.
  • tsgo --noEmit clean (exit 0).
  • oxfmt --check clean.

Risk

Low. Behavioral change is scoped to the error path of a bookkeeping function. Callers that previously got an unhandled rejection on EPERM now get a resolved promise, which is the intended outcome. Profile state in memory stays authoritative; disk just gets slightly stale until the next successful write.

AI disclosure

This change was drafted with Claude Code acting as coding assistant. The issue was picked from the triaged backlog, the root-cause analysis and implementation plan were produced interactively, and tests were written against the exact stack trace in the issue report. Human review of the patch and regression tests before submission.

Understanding confirmation

Yes, I've read CONTRIBUTING.md and VISION.md. This is a single-concern bug fix with no dependency updates, no schema changes, and no new public APIs. The affected module is src/agents/auth-profiles/. No Carbon changes, no @ts-nocheck, no lint suppression.

@ademczuk
ademczuk requested a review from a team as a code owner April 15, 2026 07:59
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: M labels Apr 15, 2026
@greptile-apps

greptile-apps Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Wraps the bodies of markAuthProfileGood, markAuthProfileUsed, and markAuthProfileFailure in try/catch blocks so that EPERM (or any other) save failures during post-completion bookkeeping are logged as warnings rather than propagated, fixing a Windows gateway-unresponsive regression (#62099). The three new regression tests cover all three entry points by mocking both save paths to throw EPERM and asserting the functions resolve normally.

Confidence Score: 5/5

Safe to merge — targeted error-path change with zero impact on the happy path and full regression test coverage.

All three modified functions are post-completion bookkeeping with no caller-visible contract on the error path. The try/catch scope is tight, in-memory state degradation on EPERM is intentional and documented, and the test file directly exercises the exact failure mode from the issue. No P0 or P1 findings.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(auth-profiles): make post-success bo..." | Re-trigger Greptile

@ademczuk

Copy link
Copy Markdown
Contributor Author

Closing to recreate from a clean base. The branch was cut from a stale fork main and went DIRTY on submission. Will resubmit from upstream HEAD with the same fix.

@ademczuk ademczuk closed this Apr 15, 2026
@ademczuk
ademczuk deleted the fix-auth-profiles-eperm-non-fatal branch April 15, 2026 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EPERM on auth-profiles.json causes full gateway failure cascade (Windows)

1 participant