Budibase/budibase · error · HTTPError
Slack app configuration token has expired. Save a new config
Error message
Slack app configuration token has expired. Save a new config token and refresh token.
What it means
fetchConfigToken tries to auto-rotate an expired (or near-expiry) config token using the stored refresh token. If the token needs rotation, there is no refreshToken, and an expiresAt is present (so the token is known-expired), rotation is impossible and this 400 HTTPError is thrown, instructing the user to re-enter both tokens.
Source
Thrown at packages/server/src/sdk/workspace/ai/slackAppConfig.ts:94
}
}
export const fetchConfigToken = async () => {
const config = await fetch()
if (!config?.configToken) {
throw new HTTPError(
"Slack app configuration token is not configured for this workspace",
400
)
}
if (!tokenNeedsRotation(config.expiresAt) && config.expiresAt) {
return decodeSecret(config.configToken)
}
if (!config.refreshToken) {
if (config.expiresAt) {
throw new HTTPError(
"Slack app configuration token has expired. Save a new config token and refresh token.",
400
)
}
return decodeSecret(config.configToken)
}
const rotated = await rotateSlackConfigToken({
refreshToken: decodeSecret(config.refreshToken),
})
return decodeSecret((await saveRotatedConfig(config, rotated)).configToken)
}
export const save = async (configToken: string, refreshToken: string) => {
const db = getWorkspaceDB()
const existing = await fetch()
const now = new Date().toISOString()
const trimmedToken = configToken.trim()View on GitHub (pinned to a81a902e9a)
Solutions
- Re-save both a new config token and a new refresh token via the Slack app config save endpoint
- Generate fresh tokens from the Slack app configuration source and update the workspace config
- If a refresh token exists elsewhere, save it so auto-rotation can work
- Add validation at save time to require a refresh token so this state cannot occur
Example fix
// before
{ configToken: "bbai_enc::...", expiresAt: "2026-08-01T00:00:00Z" } // no refreshToken
// after
await save(newConfigToken, newRefreshToken)
// results in
{ configToken: "bbai_enc::...", refreshToken: "bbai_enc::...", expiresAt: "2026-09-01T00:00:00Z" } Defensive patterns
Strategy: validation
Validate before calling
const config = await slackAppConfig.fetch()
const expired = config?.expiresAt && new Date(config.expiresAt).getTime() <= Date.now() + 5 * 60 * 1000
if (expired && !config?.refreshToken) {
throw new Error("Config token expired and no refresh token saved; re-save both tokens")
} Type guard
const isRefreshable = (
config: SlackAppConfig | undefined
): config is SlackAppConfig & { refreshToken: string } =>
typeof config?.refreshToken === "string" && config.refreshToken.length > 0 Try / catch
try {
const token = await fetchConfigToken()
} catch (err) {
if (err instanceof HTTPError && err.status === 400 && err.message.includes("has expired")) {
// prompt user to save a new config token and refresh token
} else { throw err }
} Prevention
- Always save both config token and refresh token together
- Never clear the refresh token when updating the config token
- Monitor expiresAt and refresh proactively before the 5-minute buffer
- Require refreshToken at save time to prevent unrecoverable states
When it happens
Trigger: Config token's expiresAt is within the 5-minute rotation buffer or past, config.refreshToken is absent, and configToken itself is expired — the no-refresh-token + expired-token combination.
Common situations: User saved only a config token without a refresh token; refresh token was cleared/removed from the config; token aged past expiry while refresh token was lost; legacy config saved before refresh tokens were required.
Related errors
- Slack OAuth client credentials are not configured
- Slack app configuration token has expired. Save a new config
- Slack app configuration requires a development workspace
- Slack app configuration token is not configured for this wor
- Workspace DB not found - self-host users using cloud don't h
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/2c36f53b1976cae1.
Report an issue: GitHub.