mastra-ai/mastra · error

SlackProvider baseUrl not available during OAuth callback

Error message

SlackProvider baseUrl not available during OAuth callback

What it means

During the Slack OAuth callback, the provider needs its own base URL to build redirect_uri and continue the flow. #getBaseUrl() returned nothing (provider never had baseUrl set and it couldn't be inferred from the request), so the provider throws rather than constructing a wrong redirect URI. This is an internal invariant: the OAuth exchange at slack.com/api/oauth.v2.access requires a redirect_uri that matches the one used to start the flow.

Source

Thrown at channels/slack/src/provider.ts:1406

    // Decrypt secrets for use
    const pending = this.#decryptPendingInstallation(pendingEncrypted);

    if (error) {
      const errorUrl = pending.redirectUrl ?? this.#channelConfig.redirectPath ?? '/';
      const redirect = new URL(errorUrl, c.req.url);
      redirect.searchParams.set('channel_error', error);
      redirect.searchParams.set('platform', 'slack');
      return c.redirect(redirect.toString());
    }

    if (!code) {
      return c.json({ error: 'Missing code parameter' }, 400);
    }

    const baseUrl = this.#getBaseUrl();
    if (!baseUrl) {
      throw new Error('SlackProvider baseUrl not available during OAuth callback');
    }

    try {
      // Exchange code for tokens
      const tokenResponse = await fetch('https://slack.com/api/oauth.v2.access', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({
          client_id: pending.clientId,
          client_secret: pending.clientSecret,
          code,
          redirect_uri: `${baseUrl}/slack/oauth/callback`,
        }),
        signal: AbortSignal.timeout(30_000),
      });

      if (!tokenResponse.ok) {
        throw new Error(`Slack OAuth HTTP error: ${tokenResponse.status} ${tokenResponse.statusText}`);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass an explicit baseUrl: provider.configure({ baseUrl: 'https://your-domain.com' }) or via the constructor.
  2. Configure your proxy to forward Host and X-Forwarded-Proto headers so the server can infer the base URL.
  3. Ensure the callback is reached via a normal HTTP request to the registered route, not invoked programmatically.
  4. Verify the Slack app's redirect URL matches the same baseUrl used at install time.

Example fix

// before
const provider = new SlackProvider({ refreshToken });
// after
const provider = new SlackProvider({
  refreshToken,
  baseUrl: process.env.PUBLIC_BASE_URL ?? 'https://myapp.example.com',
});
Defensive patterns

Strategy: validation

Validate before calling

const baseUrl = process.env.PUBLIC_BASE_URL;
if (!baseUrl) throw new Error('PUBLIC_BASE_URL must be set for Slack OAuth callbacks');
provider.configure({ baseUrl });

Try / catch

try {
  await handleSlackCallback(req);
} catch (e) {
  if (e instanceof Error && e.message.includes('baseUrl not available')) {
    console.error('Set baseUrl on SlackProvider / fix proxy forwarded headers');
  } else throw e;
}

Prevention

When it happens

Trigger: Hitting the /slack/oauth/callback route while the SlackProvider's baseUrl is unset — e.g. provider mounted without configure({ baseUrl }) and the server can't derive the origin (proxied setup without forwarded headers, non-HTTP transport, or a callback invoked outside a normal HTTP request context).

Common situations: Running behind a reverse proxy that strips Host/X-Forwarded-* headers; testing the callback endpoint directly with curl (no Origin/host context); forgetting to pass baseUrl when constructing the provider for a deployment behind a custom domain.

Related errors


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