mastra-ai/mastra · error

Token rotation failed: ${data.error}

Error message

Token rotation failed: ${data.error}

What it means

This is the generic failure branch of Slack token rotation: the API responded with ok:false and an error string that is not the special-cased invalid_refresh_token. The client surfaces Slack's own error verbatim in the message.

Source

Thrown at channels/slack/src/client.ts:90

      }),
      signal: AbortSignal.timeout(SLACK_API_TIMEOUT_MS),
    });

    const data = (await response.json()) as {
      ok: boolean;
      error?: string;
      token?: string;
      refresh_token?: string;
    };

    if (!data.ok) {
      if (data.error === 'invalid_refresh_token') {
        throw new Error(
          '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.',
        );
      }
      throw new Error(`Token rotation failed: ${data.error}`);
    }

    if (!data.token || !data.refresh_token) {
      throw new Error('Token rotation returned incomplete data');
    }

    this.#token = data.token;
    this.#refreshToken = data.refresh_token;

    if (this.#onTokenRotation) {
      await this.#onTokenRotation({
        token: this.#token,
        refreshToken: this.#refreshToken,
      });
    }
  }

  /**

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Read the Slack error in the message and act on it (e.g. expired_refresh_token → get fresh tokens from the Slack app config)
  2. Add retry with backoff for transient errors (rateLimited, server errors) in your wrapper around the channel client
  3. Verify the app's client ID/secret and that stored tokens are current; re-install the app if rotation is permanently broken

Example fix

// before
try { await client.rotateToken(); } catch (e) { /* unhandled */ }
// after
try { await client.rotateToken(); } catch (e) {
  logger.error({ err: e }, 'slack token rotation failed');
  if (isTransient(e)) await retryWithBackoff(() => client.rotateToken());
  else await alertOperatorForReinstall();
}
Defensive patterns

Strategy: retry

Try / catch

try {
  await client.rotateToken();
} catch (e) {
  const msg = (e as Error).message;
  if (msg.startsWith('Token rotation failed:') && /rateLimited|temporarily|server/i.test(msg)) {
    await backoffRetry(() => client.rotateToken(), 3);
  } else if (msg.startsWith('Slack refresh token is invalid')) {
    await alertForFreshTokens();
  } else throw e;
}

Prevention

When it happens

Trigger: #doRotateToken receives { ok: false, error: <some error> } from Slack's rotation endpoint (e.g. expired_refresh_token, invalid_client, rateLimited) during rotateToken().

Common situations: Expired refresh tokens after long downtime; wrong client credentials/env in the rotation request; Slack API rate limits or transient outages; malformed stored tokens after manual DB edits.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/4f0134e33c18969b. Report an issue: GitHub.