Budibase/budibase · error
Error fetching oauth2 token: ${message}
Error message
Error fetching oauth2 token: ${message} What it means
fetchAndParseToken POSTs client-credentials to the OAuth2 token endpoint. If the provider responds with a non-OK status, the thrown plain Error surfaces the provider's error_description (or HTTP statusText). This means the token request itself was rejected by the identity provider, not that a Budibase config was missing.
Source
Thrown at packages/server/src/sdk/workspace/oauth2/utils.ts:95
await writethrough.patch({
lastUsage: Date.now(),
})
}
async function fetchAndParseToken(config: {
url: string
clientId: string
clientSecret: string
method: OAuth2CredentialsMethod
grantType: OAuth2GrantType
scope?: string
audience?: string
}): Promise<{ value: string; ttl: number }> {
const resp = await fetchToken(config)
const jsonResponse = await resp.json()
if (!resp.ok) {
const message = jsonResponse.error_description ?? resp.statusText
throw new Error(`Error fetching oauth2 token: ${message}`)
}
const token = `${jsonResponse.token_type} ${jsonResponse.access_token}`
const ttl = jsonResponse.expires_in ?? -1
return { value: token, ttl }
}
export async function getToken(id: string) {
const token = await cache.withCacheWithDynamicTTL(
cache.CacheKey.OAUTH2_TOKEN(id),
async () => {
const config = await get(id)
if (!config) {
throw new HTTPError(`oAuth config ${id} could not be found`, 400)
}
return fetchAndParseToken(config)
}
)
View on GitHub (pinned to a81a902e9a)
Solutions
- Verify clientId/clientSecret and grant type against the provider's settings
- Check the token URL is the exact token endpoint (e.g. https://provider/oauth/token)
- Ensure required scope/audience values are configured
- Read the error_description in the message — it names the provider's exact rejection reason
- Confirm env-var placeholders used in the config resolve to non-empty values
Example fix
// before
// config.clientSecret stored as {{env.SECRET}} but env var missing
// after
// set the env var (or hardcode a valid secret in dev) and redeploy so
// processEnvironmentVariable resolves a real secret before the token POST Defensive patterns
Strategy: try-catch
Validate before calling
if (!config.url || !config.clientId || !config.clientSecret) {
throw new Error('OAuth2 config incomplete: url, clientId and clientSecret are required')
} Type guard
null
Try / catch
try {
const token = await getToken(configId)
} catch (e) {
if (/^Error fetching oauth2 token:/.test(String(e.message))) {
// provider rejected the request: log e.message (contains error_description),
// fix credentials/URL/scope, and apply backoff before retrying
} else throw e
} Prevention
- Validate the token URL is the exact provider token endpoint
- Rotate and re-test client secrets after provider changes
- Set required audience/scope values for providers like Auth0
- Verify env-var placeholders resolve to non-empty values before deploying
When it happens
Trigger: Calling getToken(id) (or getTokenFromConfig) where fetchToken receives a 4xx/5xx response: wrong client_id/client_secret, unsupported grant_type, invalid scope/audience, token URL pointing at a non-token endpoint, or the endpoint returning an error body that is not JSON-encodable errors.
Common situations: Expired or rotated client secrets after provider-side changes; Auth0-style providers requiring an audience parameter that was not set; scope names changed by the provider; environment-variable substitution producing an empty secret; firewalls/proxies returning HTML error pages that break resp.json().
Related errors
- Unexpected response when fetching openid-configuration: ${re
- unexpected response ${response.statusText}
- Unexpected response ${response.statusText}
- Failed to retrieve skeleton metadata
- Unable to retrieve docker-compose file - ${response.status}
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/dffca3e0e6d7c6cf.
Report an issue: GitHub.