Skip to content

Bug: concurrent commands race OAuth refresh and Keychain updates #202

Description

Summary

When multiple cx processes use the same OAuth profile concurrently at the point where the access token needs refreshing, token refresh and OS-keyring persistence appear to race.

One process succeeds, while sibling processes may report either invalid_grant or a Keychain duplicate-item error. A subsequent serialized cx command succeeds without reauthentication, so the profile is not actually unauthenticated.

Environment

  • cx 0.1.16
  • macOS
  • OAuth profile with tokens stored in the OS keyring
  • Observed with read-only telemetry commands using the same profile

Reproduction

  1. Configure an OAuth profile using OS-keyring storage.
  2. Reach a state where the access token needs refreshing while the refresh token is valid.
  3. Launch several authenticated commands concurrently against the same profile, for example:
for n in 1 2 3 4; do
  cx logs 'source logs' --start now-5m --limit 1 --read-only --output agents \
    > "/tmp/cx-$n.out" 2>&1 &
done
wait

This is especially easy to trigger in agent workflows that perform telemetry discovery in parallel.

Actual behavior

Some commands fail with:

Configuration error: OAuth token refresh failed for profile '<profile>'.
...
Token refresh failed (400 Bad Request):
{"error":"invalid_grant","error_description":"The provided authorization grant is invalid."}

Another concurrent process may fail with:

Configuration error: Failed to store secrets in keyring
...
Platform secure storage failure: The specified item already exists in the keychain.

At least one concurrent command can still succeed. Running another authenticated command serially afterward also succeeds without running cx profiles add, showing that one process stored a valid refreshed token.

Expected behavior

Concurrent commands sharing an OAuth profile should coordinate refresh so that:

  • only one process exchanges the refresh token;
  • waiting processes reread the newly stored tokens after the refresh completes;
  • keyring persistence does not fail on an existing item;
  • all commands proceed with the valid access token.

Likely cause

If the OAuth provider rotates refresh tokens, multiple processes can read the same refresh token before any process stores its replacement. The first exchange succeeds and invalidates the old grant; later exchanges receive invalid_grant. Concurrent Keychain writes can independently collide.

Suggested direction

Use a cross-process lock keyed by profile around refresh and token persistence. After acquiring the lock, reread the profile/keyring state because another process may already have refreshed it. Updating/replacing keyring entries atomically—and optionally retrying once after rereading on invalid_grant—would make parallel CLI use reliable.

Impact

This produces intermittent false “reauthenticate” guidance in parallel automation and agent sessions, even though credentials remain valid. Serial use usually appears healthy, which makes the failure confusing and hard to diagnose.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions