Problem
When an account's refresh_token expires or is revoked (e.g. after Google's 7-day inactivity rotation for unverified OAuth apps, or manual revocation), the only path back to a working account is:
gmail accounts remove oscodev@gmail.com
gmail accounts add oscodev@gmail.com --manual
add errors with Error: Account 'oscodev@gmail.com' already exists if you try to re-OAuth in place — even though all the user wants is a fresh refresh_token using the same client_id/client_secret/scopes that are already on disk.
The remove step also drops the existing scopes array, so the new add has to re-prompt and re-grant scopes that were already authorized.
Proposed
gmail accounts refresh <email> [--manual]
Equivalent to remove-then-add, but:
- preserves
oauth2.clientId, oauth2.clientSecret, and scopes from the existing entry
- only re-runs the OAuth flow to mint a new
refreshToken
- atomic write back to
accounts.json (no window where the entry is missing)
Implementation is essentially add's OAuth-flow function, scoped to overwrite-by-email instead of error-on-exists.
Why it matters
Hit this in a longer-running build today — sandboxed Eva harness was using the refresh_token, hit invalid_grant, and the workaround required a 2-step manual dance. With refresh, it's one command. Also makes failure recovery scriptable (we can wrap it in a credential-rotation helper without parsing/restoring accounts.json ourselves).
Alternative
gmail accounts add <email> --force (overwrite if exists) would also work, but refresh is the more intentional verb for this specific case and surfaces the user's actual goal.
Problem
When an account's refresh_token expires or is revoked (e.g. after Google's 7-day inactivity rotation for unverified OAuth apps, or manual revocation), the only path back to a working account is:
adderrors withError: Account 'oscodev@gmail.com' already existsif you try to re-OAuth in place — even though all the user wants is a fresh refresh_token using the same client_id/client_secret/scopes that are already on disk.The
removestep also drops the existingscopesarray, so the newaddhas to re-prompt and re-grant scopes that were already authorized.Proposed
Equivalent to remove-then-add, but:
oauth2.clientId,oauth2.clientSecret, andscopesfrom the existing entryrefreshTokenaccounts.json(no window where the entry is missing)Implementation is essentially
add's OAuth-flow function, scoped to overwrite-by-email instead of error-on-exists.Why it matters
Hit this in a longer-running build today — sandboxed Eva harness was using the refresh_token, hit
invalid_grant, and the workaround required a 2-step manual dance. Withrefresh, it's one command. Also makes failure recovery scriptable (we can wrap it in a credential-rotation helper without parsing/restoringaccounts.jsonourselves).Alternative
gmail accounts add <email> --force(overwrite if exists) would also work, butrefreshis the more intentional verb for this specific case and surfaces the user's actual goal.