mastra-ai/mastra · critical

Credential storage is not available

Error message

Credential storage is not available

What it means

persistOAuthCredential stores OAuth tokens either in the tenant in-memory store or, as a fallback, in a configured authStorage. When neither exists (authStorage is null) the credential would be silently lost, so the function throws instead. It's a configuration error: the factory was assembled without credential storage.

Source

Thrown at mastracode/factory/src/routes/oauth.ts:231

}: {
  ctx: CredentialContext;
  provider: string;
  scope: LoginCredentialScope | undefined;
  credentials: OAuthCredentials;
  authStorage: AuthStorage | undefined;
  onCredentialsChanged: (tenant: { orgId: string; userId?: string }) => void;
}): Promise<void> {
  const authProviderId = getAuthProviderId(provider);
  if (ctx.mode === 'tenant') {
    const tenant = { orgId: ctx.orgId, ...(scope === 'org' ? {} : { userId: ctx.userId }) };
    await ctx.storage.setCredential(tenant, authProviderId, {
      type: 'oauth',
      ...credentials,
    });
    onCredentialsChanged(tenant);
    return;
  }
  if (!authStorage) throw new Error('Credential storage is not available');
  authStorage.set(authProviderId, { type: 'oauth', ...credentials });
}

async function readJsonBody(c: Context): Promise<Record<string, unknown>> {
  try {
    const body = (await c.req.json()) as unknown;
    return body && typeof body === 'object' ? (body as Record<string, unknown>) : {};
  } catch {
    return {};
  }
}

export interface OAuthRoutesDeps extends RouteDependencies {
  /** File-backed credential store; used in local (no-auth) mode. */
  authStorage?: AuthStorage;
  /** Tenant credential domain handle; absent in local (no-DB) mode. */
  modelCredentials?: ModelCredentialsStorage;
  /** Notifies the host after tenant credentials change so caches can be dropped. */

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Configure an authStorage implementation when assembling the factory routes so OAuth credentials can be persisted.
  2. Verify the factory/bootstrap code actually passes authStorage into the routes options (it may be conditionally omitted).
  3. If persistence is intentionally unsupported in your environment, pre-handle credentials via the in-memory tenant path or disable OAuth routes.
  4. In tests, provide a fake authStorage implementing get/set to satisfy the flow.

Example fix

// before
assembleFactoryApiRoutes({ /* no authStorage */ });
// after
assembleFactoryApiRoutes({
  authStorage: createAuthStorage({ driver: 'db', url: process.env.DATABASE_URL }),
});
Defensive patterns

Strategy: validation

Validate before calling

function assertCredentialStorageConfigured(opts: { authStorage?: { set(id: string, v: unknown): void } | null }) {
  if (!opts.authStorage) throw new Error('authStorage must be configured for OAuth routes');
}

Type guard

function hasAuthStorage(o: unknown): o is { authStorage: { set: (id: string, v: unknown) => void } } {
  return typeof o === 'object' && o !== null && 'authStorage' in o && (o as any).authStorage != null;
}

Try / catch

try {
  await completeOAuthFlow(...);
} catch (e) {
  if (e instanceof Error && e.message === 'Credential storage is not available') {
    logger.error('OAuth credential dropped: configure authStorage in factory options');
    return Response.json({ error: 'server misconfiguration: credential storage missing' }, { status: 500 });
  }
  throw e;
}

Prevention

When it happens

Trigger: Completing an OAuth flow (callback/token exchange route) when the factory was built without an authStorage implementation and the credential isn't handled by the in-memory tenant path.

Common situations: Deployments where persistent credential storage wasn't wired in the factory options (e.g. serverless or ephemeral environments with no storage adapter configured); forgetting to pass authStorage when calling assembleFactoryApiRoutes/routes; tests hitting OAuth routes with a stubbed factory config.

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/7d7750bf54416df0. Report an issue: GitHub.