chatboxai/chatbox · error · Error
OAuth credential missing for provider: ${chatboxProviderId}
Error message
OAuth credential missing for provider: ${chatboxProviderId} What it means
Thrown inside the credential-manager's refreshCredential closure when the in-memory `credential` is undefined at the moment a refresh is attempted. Because refreshCredential is only called from getCredential (which itself throws when credential is missing — see 238), reaching this branch implies the credential was cleared concurrently between the getCredential check and the refresh call (a race with clear()).
Source
Thrown at src/shared/oauth/credential-manager.ts:49
return undefined
}
let credential: OAuthCredentials | undefined = providerSetting.oauth
let refreshPromise: Promise<OAuthCredentials> | undefined
const persistCredential = (nextCredential: OAuthCredentials) => {
credential = nextCredential
dependencies.oauth?.persistCredential(settingsProviderId, nextCredential)
}
const clearCredential = () => {
credential = undefined
dependencies.oauth?.clearCredential(settingsProviderId)
}
const refreshCredential = async (): Promise<OAuthCredentials> => {
if (!credential) {
throw new Error(`OAuth credential missing for provider: ${chatboxProviderId}`)
}
if (!dependencies.oauth) {
return credential
}
if (!refreshPromise) {
refreshPromise = dependencies.oauth
.refreshCredential(oauthProviderId, credential)
.then((nextCredential) => {
persistCredential(nextCredential)
return nextCredential
})
.finally(() => {
refreshPromise = undefined
})
}
return refreshPromise
}
View on GitHub (pinned to 81571269ad)
Solutions
- Treat this error as 'session ended mid-refresh': discard the in-flight operation and prompt re-authentication rather than retrying.
- Serialize credential mutations (e.g. a mutex around set/clear/refresh) so refresh cannot observe a half-cleared state.
- Have getCredential hold a local reference to the credential it validated and pass it into refreshCredential, eliminating the re-read race.
- In the UI, surface a 'You've been signed out' notice when this error is caught and disable provider actions until re-login.
Example fix
// before
const refreshCredential = async (): Promise<OAuthCredentials> => {
if (!credential) {
throw new Error(`OAuth credential missing for provider: ${chatboxProviderId}`)
}
...
}
// after — capture the reference so a concurrent clear doesn't break refresh
const refreshCredential = async (): Promise<OAuthCredentials> => {
const current = credential
if (!current) {
throw new Error(`OAuth credential missing for provider: ${chatboxProviderId}`)
}
...dependencies.oauth.refreshCredential(oauthProviderId, current)...
} Defensive patterns
Strategy: try-catch
Validate before calling
// Serialize credential mutations to eliminate the refresh/clear race.
let credentialLock: Promise<unknown> = Promise.resolve()
function withCredentialLock<T>(fn: () => Promise<T>): Promise<T> {
const next = credentialLock.then(fn, fn)
credentialLock = next.catch(() => {})
return next
} Type guard
function isOAuthCredentialMissing(e: unknown, providerId: string): boolean {
return e instanceof Error && e.message === `OAuth credential missing for provider: ${providerId}`
} Try / catch
try {
return await credentialManager.getAccessToken()
} catch (e) {
if (e instanceof Error && e.message.startsWith('OAuth credential missing for provider:')) {
// session ended mid-refresh; prompt re-login, do not retry
redirectToLogin()
return
}
throw e
} Prevention
- Serialize set/clear/refresh with a mutex so refresh can't observe a half-cleared state.
- Pass the credential reference into refreshCredential rather than re-reading the field.
- Treat this specific error as terminal for the operation — never auto-retry; prompt re-authentication.
When it happens
Trigger: getCredential saw a credential and called refreshCredential; before refreshCredential's first statement ran (or in a concurrent task), clear() set credential to undefined. The synchronous guard at the top of refreshCredential then throws.
Common situations: User signed out (clear) while a token refresh was already queued; a 401 elsewhere triggered clear() racing an in-flight refresh; multi-tab logout clearing shared credential state; an explicit re-login replaced credential state and a stale refresh promise ran.
Related errors
- No content extracted from file
- Failed to search knowledge base (id: ${kbId}). Please try ag
- Knowledge base ${kbId} not found
- Invalid pagination parameters
- No authorization code found in the input
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/0cea1a55a7f9dbb5.
Report an issue: GitHub.