mastra-ai/mastra · error · Error

Session expired. Run: mastra auth login

Error message

Session expired. Run: mastra auth login

What it means

throwApiError in packages/cli/src/commands/auth/client.ts converts failed Mastra platform HTTP responses into Error objects. When the platform returns 401 (the stored token is invalid or expired and could not be refreshed), the CLI throws the fixed SESSION_EXPIRED_MESSAGE telling the user to re-authenticate with `mastra auth login`. All API wrappers (fetchOrgs, createToken, listTokensAction, revokeTokenAction, fetchProjects) funnel their failures through this function.

Source

Thrown at packages/cli/src/commands/auth/client.ts:40

 */
function deriveStudioUrl(): string {
  if (process.env.MASTRA_STUDIO_URL) return process.env.MASTRA_STUDIO_URL;
  if (MASTRA_PLATFORM_API_URL.includes('staging')) return 'https://studio.staging.mastra.ai';
  return 'https://studio.mastra.ai';
}

export const MASTRA_STUDIO_URL = deriveStudioUrl();

export const SESSION_EXPIRED_MESSAGE = 'Session expired. Run: mastra auth login';

/**
 * Throw a standardized error for API failures.
 * - 401: "Session expired" (authentication failed)
 * - Other: Show the server's error detail or fall back to status code
 */
export function throwApiError(message: string, status: number, detail?: string): never {
  if (status === 401) {
    throw new Error(SESSION_EXPIRED_MESSAGE);
  }
  if (detail) {
    throw new Error(detail);
  }
  throw new Error(`${message}: ${status}`);
}

/** Best-effort message from platform JSON error bodies (RFC 7807 `detail`, etc.). */
export function extractApiErrorDetail(error: unknown): string | undefined {
  if (!error || typeof error !== 'object') return undefined;
  const o = error as Record<string, unknown>;

  let detail: string | undefined;
  if (typeof o.detail === 'string' && o.detail.trim()) detail = o.detail;
  else if (typeof o.message === 'string' && o.message.trim()) detail = o.message;
  else if (typeof o.error === 'string' && o.error.trim()) detail = o.error;

  // Validation errors (400) carry the useful part in errors[] — field name plus

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Run `mastra auth login` to obtain fresh credentials.
  2. If using MASTRA_API_TOKEN, generate a new token in the Mastra platform dashboard and replace the env var value.
  3. Check system clock accuracy (JWT validation fails on skew) with `timedatectl` or equivalent.
  4. Delete the stale credentials file and log in again from scratch.

Example fix

// before (shell)
MASTRA_API_TOKEN=old-expired-token mastra deploys list
// after
mastra auth login
mastra deploys list
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling API
const { getToken } = await import('./credentials.js');
const token = await getToken(signal, { allowLogin: false }); // throws early if no valid auth
if (!token) throw new Error('Run `mastra auth login` first');

Type guard

function isAuthError(err: unknown, status?: number): boolean {
  return err instanceof Error &&
    (err.message.includes('Session expired') || status === 401);
}

Try / catch

try {
  await fetchOrgs(token);
} catch (err) {
  if (isAuthError(err)) {
    console.error('Session expired. Run: mastra auth login');
    process.exitCode = 1;
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Any CLI command that calls the platform API (fetchOrgs, createToken/list/revoke tokens, fetchProjects) receives HTTP 401 from the server — e.g. the cached access token in ~/.mastra credentials expired and refresh failed before the request, or the server rejected the bearer token.

Common situations: Long-lived local credentials that expired; user logged out / revoked sessions on another machine; clock skew invalidating JWTs; server rotated signing keys; MASTRA_API_TOKEN env var set to an old revoked token.

Related errors


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