toeverything/AFFiNE · error · ActionForbidden

action_forbidden

action_forbidden

Error message

You are not allowed to perform this action.

What it means

AdminGuard.canActivate (admin-guard.ts:30) throws ActionForbidden when the request's session user is not a platform administrator: allow stays false either because req.session is missing (unauthenticated/internal call) or feature.isAdmin(req.session.user.id) resolves false. It guards admin-only routes/queries/mutations with a hard 403-style failure.

Solutions

  1. Sign in as a user flagged as administrator (instance-level admin, not merely workspace owner).
  2. For self-hosted, add the user to the admin allowlist/flag in the feature/admin configuration and retry.
  3. If you do not need admin features, call the regular non-guarded endpoint instead.
Defensive patterns

Strategy: validation

Validate before calling

// server-side: check admin status before building admin responses
const isAdmin = req.session ? await featureService.isAdmin(req.session.user.id) : false;
if (!isAdmin) {
  return forbiddenFallback(); // hide admin entry points instead of triggering the guard
}

Type guard

function isAdminForbidden(e: unknown): boolean {
  return (
    e instanceof GraphQLError &&
    (e.extensions?.code === 'action_forbidden' ||
      (e as HttpException)?.status === 403)
  );
}

Try / catch

try {
  await adminMutation(input);
} catch (e) {
  if (isAdminForbidden(e)) {
    // hide admin UI and inform the user admin rights are required; never retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling any resolver/controller decorated with this admin guard while logged in as a non-admin user, or with no session at all; also when FeatureService is unavailable so isAdmin cannot confirm.

Common situations: Trying an admin GraphQL mutation or admin REST route from a normal member account on a self-hosted instance; scripts hitting admin endpoints with expired/missing auth; assuming workspace owner implies platform admin (it does not - this is instance-level admin).

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/e3c1067412edd8c9. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/common/admin-guard.ts:30

@Injectable()
export class AdminGuard implements CanActivate, OnModuleInit {
  private feature!: FeatureService;

  constructor(private readonly ref: ModuleRef) {}

  onModuleInit() {
    this.feature = this.ref.get(FeatureService, { strict: false });
  }

  async canActivate(context: ExecutionContext) {
    const { req } = getRequestResponseFromContext(context);
    let allow = false;
    if (req.session) {
      allow = await this.feature.isAdmin(req.session.user.id);
    }

    if (!allow) {
      throw new ActionForbidden();
    }

    return true;
  }
}

/**
 * This guard is used to protect routes/queries/mutations that require a user to be administrator.
 *
 * @example
 *
 * ```typescript
 * \@Admin()
 * \@Mutation(() => UserType)
 * createAccount(userInput: UserInput) {
 *   // ...
 * }
 * ```

View on GitHub (pinned to b4c8548c09)