mastra-ai/mastra · error · Error

Not logged in. Run `mastra auth login` interactively or set

Error message

Not logged in. Run `mastra auth login` interactively or set MASTRA_API_TOKEN.

What it means

getToken resolves the bearer token for platform commands: it checks MASTRA_API_TOKEN, then stored credentials. If neither exists and interactive login is disallowed (options.allowLogin === false or the process is not interactive, e.g. CI/non-TTY), it throws this error instead of launching the browser login flow, since login cannot be prompted in a non-interactive context.

Source

Thrown at packages/cli/src/commands/auth/credentials.ts:346

  return creds;
}

function isInteractive(): boolean {
  return Boolean(process.stdin.isTTY && process.stdout.isTTY) && !process.env.CI;
}

export async function getToken(signal?: AbortSignal, options: LoginOptions = {}): Promise<string> {
  signal?.throwIfAborted();

  // CI/CD headless path
  const envToken = process.env.MASTRA_API_TOKEN;
  if (envToken) return envToken;

  const creds = await loadCredentials();
  signal?.throwIfAborted();
  if (!creds) {
    if (options.allowLogin === false || !isInteractive()) {
      throw new Error('Not logged in. Run `mastra auth login` interactively or set MASTRA_API_TOKEN.');
    }
    const newCreds = await login(signal, options);
    return newCreds.token;
  }

  // Try a quick verify to see if the token is still valid.
  if (await verifyToken(creds.token, signal)) return creds.token;
  signal?.throwIfAborted();

  // Token might be expired — attempt refresh
  const refreshed = await tryRefreshToken(creds, signal);
  if (refreshed) return refreshed;
  signal?.throwIfAborted();

  if (options.allowLogin === false || !isInteractive()) {
    throw new Error('Session expired. Run `mastra auth login` interactively or set MASTRA_API_TOKEN.');
  }
  const newCreds = await login(signal, options);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Set MASTRA_API_TOKEN with a valid platform token in the environment/CI secrets.
  2. Run `mastra auth login` once in an interactive terminal so stored credentials exist.
  3. Run the command in a TTY (drop output piping) so interactive login can trigger.
  4. Check the env var is spelled MASTRA_API_TOKEN and is exported in the current shell.

Example fix

// before (CI)
- run: mastra deploy
// after
- run: mastra deploy
  env:
    MASTRA_API_TOKEN: ${{ secrets.MASTRA_API_TOKEN }}
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.MASTRA_API_TOKEN && !isTTY()) {
  throw new Error('Set MASTRA_API_TOKEN for non-interactive use, or run `mastra auth login` in a terminal');
}

Try / catch

try {
  const token = await getToken(signal, { allowLogin: false });
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Not logged in')) {
    console.error('Provide MASTRA_API_TOKEN or authenticate interactively first.');
    process.exitCode = 1;
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: A command calls getToken with no MASTRA_API_TOKEN set and no credentials file, while options.allowLogin is false or isInteractive() is false (piped output, CI runner, non-TTY shell).

Common situations: CI pipelines missing the MASTRA_API_TOKEN secret; running the CLI from a script or Docker container without a TTY; fresh environments without `mastra auth login`; env var typo'd (MASTRA_API_KEY instead of MASTRA_API_TOKEN).

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


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