mastra-ai/mastra · error · HTTPException

FGA authorization denied: authenticated user is required

Error message

FGA authorization denied: authenticated user is required

What it means

When a Fine-Grained Authorization (FGA) provider is configured, enforceThreadAccess requires an authenticated user object in requestContext to evaluate thread permissions. If no user is present (auth disabled or hook did not populate it), the check cannot run, so it fails closed with this 403 rather than granting access.

Source

Thrown at packages/server/src/server/handlers/utils.ts:153

  permission = MastraFGAPermissions.MEMORY_READ,
}: {
  mastra: any;
  requestContext?: RequestContext;
  threadId: string;
  thread?: { resourceId?: string | null } | null;
  effectiveResourceId?: string;
  permission?: MastraFGAPermissionInput;
}): Promise<void> {
  await validateThreadOwnership(thread, effectiveResourceId);

  const fgaProvider = mastra?.getServer?.()?.fga;
  if (!fgaProvider) {
    return;
  }

  const user = requestContext?.get('user');
  if (!user || typeof user !== 'object') {
    throw new HTTPException(403, { message: 'FGA authorization denied: authenticated user is required' });
  }

  await MastraMemory.checkThreadFGA({
    mastra,
    user: user as { id: string; [key: string]: unknown },
    threadId,
    resourceId: thread?.resourceId ?? effectiveResourceId,
    requestContext,
    permission,
  });
}

/**
 * Validates that a workflow run belongs to the specified resourceId.
 * Throws 403 if the run exists but belongs to a different resource.
 */
export async function validateRunOwnership(
  run: { resourceId?: string | null } | null | undefined,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Enable and correctly configure server authentication so a user object is attached to requestContext for every request.
  2. In custom auth, ensure you call requestContext.set('user', { id, ... }) after validating credentials.
  3. Use an authenticated client (valid token/credentials) when calling FGA-protected routes.
  4. Temporarily disable the FGA provider in non-production if unauthenticated internal calls are required.

Example fix

// before
// custom auth verifies token but never sets user
if (token) next();
// after
const user = await verifyToken(token);
requestContext.set('user', { id: user.sub, email: user.email });
next();
Defensive patterns

Strategy: validation

Validate before calling

function requireAuthenticatedUser(requestContext: { get: (k: string) => unknown }): asserts requestContext is { get: (k: 'user') => { id: string } } {
  const user = requestContext.get('user');
  if (!user || typeof user !== 'object' || typeof (user as any).id !== 'string') {
    throw new Error('FGA-protected routes require an authenticated user attached to requestContext');
  }
}

Type guard

function hasUser(ctx: { get: (k: string) => unknown }): boolean {
  const u = ctx.get('user');
  return !!u && typeof u === 'object' && typeof (u as any).id === 'string';
}

Try / catch

try {
  const res = await fetch('/api/agents/assistant/generate', { method: 'POST', headers: authHeaders, body: JSON.stringify(payload) });
  if (res.status === 403 && (await res.text()).includes('FGA authorization denied')) {
    throw new Error('Ensure server auth is enabled and the user is attached to requestContext when FGA is configured');
  }
  return await res.json();
} catch (e) { throw e; }

Prevention

When it happens

Trigger: Calling generate/stream/list-suspended/resume routes on a Mastra server with an FGA provider configured, where requestContext has no `user` object — typically because authentication is disabled or the auth hook failed to attach the user.

Common situations: Enabling FGA (e.g. OpenFGA/SpiceDB integration) while leaving server auth disabled; a custom auth plugin that validates tokens but never sets requestContext.set('user', ...); calling routes with an unauthenticated internal service client.

Understand the failure class

Related errors


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