Summary
On macOS Keychain, a token refresh can successfully persist the refreshed access token/expiry, but gog still logs:
time=2026-06-09T17:00:43.727+02:00 level=WARN msg="persist refreshed token metadata failed" [email protected] client=default err="store token: set token: Failed to update item in keychain: The specified item already exists in the keychain. (-25299)"
This warning appears misleading: immediately after the warning, exporting the token shows the refreshed expiry was stored, and subsequent auth-using commands do not warn again until the next refresh window.
Environment
- OS: macOS / Darwin arm64
gog: v0.21.0 (2c84d89 2026-06-01T21:54:29Z)
- Keyring backend: macOS Keychain/default backend
- Account/client: default OAuth client, Gmail/Calendar scopes among others
I also checked the relevant code path in v0.21.0, v0.22.0, v0.23.0, and main; the warning path appears unchanged.
Repro / observed behavior
Before expiry, commands do not warn. Token export showed:
access_exp=2026-06-09T15:00:14Z
At/after expiry, the first auth-using command refreshes and logs the warning:
gog gmail list 'is:inbox is:unread'
Output starts with:
time=2026-06-09T17:00:43.727+02:00 level=WARN msg="persist refreshed token metadata failed" [email protected] client=default err="store token: set token: Failed to update item in keychain: The specified item already exists in the keychain. (-25299)"
But immediately afterwards, repeated auth-using commands succeed without the warning:
gog gmail list 'is:inbox is:unread'
gog gmail list 'is:inbox is:unread'
and exporting the token shows that the new access token expiry was persisted:
access_exp=2026-06-09T16:00:42Z
So the runtime behavior looks like:
- stored access token expires;
- first command refreshes successfully;
SetToken returns -25299 from macOS Keychain and client_auth.go logs persist refreshed token metadata failed;
- nevertheless the refreshed expiry is visible in the stored token;
- subsequent commands do not refresh/warn until the next expiry.
Why this seems wrong/misleading
The warning sounds like the refreshed token metadata was not persisted at all, which would imply every new gog process after expiry should refresh and warn again. That is not what happens: the refreshed expiry is visible in gog auth tokens export, and subsequent commands use it.
Looking at internal/secrets/store.go, SetToken writes multiple entries/aliases: primary token, legacy token for the default client, and subject-token alias when a subject is present. A plausible failure mode is that the primary token write succeeds, but a later alias write returns errSecDuplicateItem (-25299). If so, the current warning is too broad: it reports the whole refresh persist as failed, even though the primary token appears to have persisted.
Expected behavior
One of:
- handle macOS
errSecDuplicateItem during refresh alias writes by updating/replacing/retrying the specific item; or
- make the warning more precise, e.g. distinguish primary token persistence from legacy/subject alias persistence; or
- if
-25299 is benign after read-back verification confirms the primary token was updated, downgrade/suppress the warning.
Relevant code path
internal/googleapi/client_auth.go:
if err := p.store.SetToken(p.client, persistEmail, updated); err != nil {
slog.Warn("persist refreshed token metadata failed", "email", persistEmail, "client", p.client, "err", err)
return t, nil
}
internal/secrets/store.go writes primary/legacy/subject token entries via verifiedSet(...). The observed error does not identify which of those writes failed, only:
store token: set token: Failed to update item in keychain: The specified item already exists in the keychain. (-25299)
Summary
On macOS Keychain, a token refresh can successfully persist the refreshed access token/expiry, but
gogstill logs:This warning appears misleading: immediately after the warning, exporting the token shows the refreshed expiry was stored, and subsequent auth-using commands do not warn again until the next refresh window.
Environment
gog:v0.21.0 (2c84d89 2026-06-01T21:54:29Z)I also checked the relevant code path in
v0.21.0,v0.22.0,v0.23.0, andmain; the warning path appears unchanged.Repro / observed behavior
Before expiry, commands do not warn. Token export showed:
At/after expiry, the first auth-using command refreshes and logs the warning:
gog gmail list 'is:inbox is:unread'Output starts with:
But immediately afterwards, repeated auth-using commands succeed without the warning:
and exporting the token shows that the new access token expiry was persisted:
So the runtime behavior looks like:
SetTokenreturns-25299from macOS Keychain andclient_auth.gologspersist refreshed token metadata failed;Why this seems wrong/misleading
The warning sounds like the refreshed token metadata was not persisted at all, which would imply every new
gogprocess after expiry should refresh and warn again. That is not what happens: the refreshed expiry is visible ingog auth tokens export, and subsequent commands use it.Looking at
internal/secrets/store.go,SetTokenwrites multiple entries/aliases: primary token, legacy token for the default client, and subject-token alias when a subject is present. A plausible failure mode is that the primary token write succeeds, but a later alias write returnserrSecDuplicateItem(-25299). If so, the current warning is too broad: it reports the whole refresh persist as failed, even though the primary token appears to have persisted.Expected behavior
One of:
errSecDuplicateItemduring refresh alias writes by updating/replacing/retrying the specific item; or-25299is benign after read-back verification confirms the primary token was updated, downgrade/suppress the warning.Relevant code path
internal/googleapi/client_auth.go:internal/secrets/store.gowrites primary/legacy/subject token entries viaverifiedSet(...). The observed error does not identify which of those writes failed, only: