{"record":{"id":"84c861137f44c4de","repo":"mastra-ai/mastra","slug":"slack-refresh-token-is-invalid-get-fresh-tokens-f","errorCode":null,"errorMessage":"Slack refresh token is invalid. Get fresh tokens from https://api.slack.com/apps > \"Your App Configuration Tokens\". This can happen if storage was lost or the token was already used.","messagePattern":"Slack refresh token is invalid\\. Get fresh tokens from https://api\\.slack\\.com/apps > \"Your App Configuration Tokens\"\\. This can happen if storage was lost or the token was already used\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"channels/slack/src/client.ts","lineNumber":85,"sourceCode":"      headers: {\n        'Content-Type': 'application/x-www-form-urlencoded',\n      },\n      body: new URLSearchParams({\n        refresh_token: this.#refreshToken,\n      }),\n      signal: AbortSignal.timeout(SLACK_API_TIMEOUT_MS),\n    });\n\n    const data = (await response.json()) as {\n      ok: boolean;\n      error?: string;\n      token?: string;\n      refresh_token?: string;\n    };\n\n    if (!data.ok) {\n      if (data.error === 'invalid_refresh_token') {\n        throw new Error(\n          'Slack refresh token is invalid. Get fresh tokens from https://api.slack.com/apps > \"Your App Configuration Tokens\". ' +\n            'This can happen if storage was lost or the token was already used.',\n        );\n      }\n      throw new Error(`Token rotation failed: ${data.error}`);\n    }\n\n    if (!data.token || !data.refresh_token) {\n      throw new Error('Token rotation returned incomplete data');\n    }\n\n    this.#token = data.token;\n    this.#refreshToken = data.refresh_token;\n\n    if (this.#onTokenRotation) {\n      await this.#onTokenRotation({\n        token: this.#token,\n        refreshToken: this.#refreshToken,","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/channels/slack/src/client.ts#L67-L103","documentation":"The Slack channel client rotates OAuth access tokens using a stored refresh token. When Slack responds with error 'invalid_refresh_token', the stored refresh token is no longer usable — it may have been consumed by a prior rotation or lost when storage was wiped. The client throws this explicit message to tell the operator to re-fetch tokens from the Slack app config.","triggerScenarios":"#doRotateToken POSTs to Slack's token rotation endpoint and receives { ok: false, error: 'invalid_refresh_token' } — i.e. rotateToken() was invoked (usually after an access token expired) while the persisted refresh token was wrong, empty, or already used.","commonSituations":"Slack's one-time-use refresh tokens with multiple app instances racing to rotate; wiping/resetting the storage layer (KV/DB) that held the tokens; restoring an old backup with stale tokens; rotating manually in the Slack dashboard so the stored token was invalidated.","solutions":["Go to https://api.slack.com/apps > Your App Configuration Tokens and generate fresh tokens, then update them in your storage/config","Ensure only one app instance performs rotation (avoid concurrent rotations consuming one-time refresh tokens)","Verify the storage backend persists tokens across restarts and that you are not pointing at an empty/stale store"],"exampleFix":"// before\n// storage lost tokens; rotation fails with invalid_refresh_token\n// after\nawait slackClient.setTokens(newAccessToken, newRefreshToken); // fresh tokens from Slack app config","handlingStrategy":"retry","validationCode":"const refreshToken = await storage.get('slack_refresh_token');\nif (!refreshToken || refreshToken.length < 20) {\n  promptOperatorForFreshTokens(); // avoid guaranteed invalid_refresh_token\n}","typeGuard":null,"tryCatchPattern":"try {\n  await client.rotateToken();\n} catch (e) {\n  if ((e as Error).message.startsWith('Slack refresh token is invalid')) {\n    // only recovery: fetch fresh tokens from https://api.slack.com/apps and store them\n  } else throw e;\n}","preventionTips":["Single-writer rule: only one instance performs token rotation (Slack refresh tokens are one-time-use)","Persist tokens in durable storage; never run with a wiped/ephemeral store in production","Rotate proactively before access-token expiry to reduce race windows","Alert on rotation failures so you can refresh tokens from the Slack dashboard quickly"],"tags":["oauth","slack","token-rotation","storage"],"backgroundTag":"oauth-refresh-token-invalid","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}