Budibase/budibase · error · HTTPError

Failed to rotate Slack app configuration token

Error message

Failed to rotate Slack app configuration token

What it means

This error is thrown by rotateSlackConfigToken when the HTTP response from Slack's app configuration token rotation endpoint is non-ok. Token rotation exchanges a refresh token for a new config token + refresh token pair; an HTTP-level failure means the rotation request was rejected by Slack before producing a structured API payload.

Source

Thrown at packages/server/src/sdk/workspace/ai/deployments/slack.ts:263

}

export const rotateSlackConfigToken = async ({
  refreshToken,
}: {
  refreshToken: string
}) => {
  const response = await fetch(`${SLACK_API_BASE}/tooling.tokens.rotate`, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      refresh_token: refreshToken,
    }),
  })
  const payload = (await response.json()) as SlackConfigTokenRotateResponse
  if (!response.ok) {
    throw new HTTPError(
      "Failed to rotate Slack app configuration token",
      response.status
    )
  }

  const rotated = assertSlackOk(
    payload,
    "Failed to rotate Slack app configuration token"
  )
  if (!rotated.token || !rotated.refresh_token || !rotated.exp) {
    throw new HTTPError(
      "Slack app configuration token rotation response was incomplete",
      400
    )
  }

  return {
    configToken: rotated.token,

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Confirm a non-empty refresh token is being passed - inspect the stored value before calling rotateSlackConfigToken.
  2. Pass the refresh token (not the config/access token) to the rotation endpoint.
  3. If the Slack app was deleted, redo the Slack app setup to get a new config/refresh token pair.
  4. For 5xx statuses, retry the rotation after a short delay.

Example fix

// before
await rotateSlackConfigToken({ refreshToken: undefined }) // HTTP 400
// after
if (!stored.refreshToken) throw new Error("Refresh Slack config token via setup first")
const rotated = await rotateSlackConfigToken({ refreshToken: stored.refreshToken })
await persist({ configToken: rotated.configToken, refreshToken: rotated.refreshToken })
Defensive patterns

Strategy: try-catch

Validate before calling

if (!refreshToken?.trim()) {
  throw new Error("Cannot rotate Slack config token: refresh token missing; redo Slack setup")
}

Try / catch

try {
  const rotated = await rotateSlackConfigToken({ refreshToken })
  await persist(rotated) // save both new configToken and refreshToken
  return rotated
} catch (e) {
  if (e instanceof HTTPError && e.status >= 500) {
    await sleep(5000) // transient Slack issue, safe to retry
    return rotateSlackConfigToken({ refreshToken })
  }
  if (e instanceof HTTPError && e.status === 400) {
    throw new Error("Refresh token rejected; repeat the Slack app setup")
  }
  throw e
}

Prevention

When it happens

Trigger: POSTing refresh_token to Slack's config token rotate endpoint and receiving 4xx/5xx - e.g. 400/401 from a malformed or empty refresh_token field, wrong endpoint/auth, or 5xx from Slack.

Common situations: The stored refresh token was emptied by a partial save or env var absence (empty string posted); Slack app deleted so the token's parent no longer exists; transient Slack outage; calling rotation with an access token instead of a refresh token.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/6ac3a466bff59ba2. Report an issue: GitHub.