nestjs/nest · error · RpcException
Forbidden resource
Error message
Forbidden resource
What it means
Thrown as an RpcException wrapping FORBIDDEN_MESSAGE ('Forbidden resource') from createGuardsFn() inside rpc-context-creator.ts. When an RPC handler is protected by one or more guards (@UseGuards), NestJS invokes each guard's canActivate(); if any returns false (or resolves falsy) the framework throws this RpcException, which the RPC exception filter then serializes to the client as an error status with the permission/forbidden semantics.
Source
Thrown at packages/microservices/context/rpc-context-creator.ts:162
return Reflect.getMetadata(PARAMTYPES_METADATA, instance, callback.name);
}
public createGuardsFn<TContext extends string = ContextType>(
guards: any[],
instance: Controller,
callback: (...args: unknown[]) => unknown,
contextType?: TContext,
): Function | null {
const canActivateFn = async (args: any[]) => {
const canActivate = await this.guardsConsumer.tryActivate<TContext>(
guards,
args,
instance,
callback,
contextType,
);
if (!canActivate) {
throw new RpcException(FORBIDDEN_MESSAGE);
}
};
return guards.length ? canActivateFn : null;
}
public getMetadata<TMetadata, TContext extends ContextType = ContextType>(
instance: Controller,
methodName: string,
defaultCallMetadata: Record<string, any>,
contextType: TContext,
): RpcHandlerMetadata {
const cacheMetadata = this.handlerMetadataStorage.get(instance, methodName);
if (cacheMetadata) {
return cacheMetadata;
}
const metadata =
this.contextUtils.reflectCallbackMetadata<TMetadata>(
instance,View on GitHub (pinned to 6ec0e2783d)
Solutions
- Verify the guard's canActivate() logic returns true for authorized callers — inspect token/role extraction.
- Ensure the client sends the credentials/metadata the guard expects (e.g. gRPC metadata, MQTT/RMQ headers) so canActivate() can succeed.
- Make the guard return an explicit boolean (true), not undefined/null/0, for the allow path.
- If the denial is expected, handle RpcException with status permission-denied on the client side.
Example fix
// before
@Injectable()
class RoleGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean {
const req = ctx.switchToRpc().getContext();
return req.user?.role === 'admin'; // undefined -> false -> 'Forbidden resource'
}
}
// after
@Injectable()
class RoleGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean {
const req = ctx.switchToRpc().getContext();
return Boolean(req.user && req.user.role === 'admin');
}
}
// and on the client: send the auth metadata so user is populated Defensive patterns
Strategy: try-catch
Validate before calling
// No pre-call validation possible from the caller for a guard's decision;
// instead ensure the caller sends the credentials the guard inspects.
function withAuthMetadata<T>(payload: T, token: string) {
return { data: payload, metadata: { authorization: `Bearer ${token}` } };
} Type guard
import { RpcException } from '@nestjs/microservices';
const isForbiddenRpc = (e: unknown): boolean =>
e instanceof RpcException && /Forbidden resource/.test(e?.message ?? ''); Try / catch
try {
await firstValueFrom(client.send('getUser', payload));
} catch (e) {
if (isForbiddenRpc(e)) {
// redirect to login / refresh token / surface 403
} else throw e;
} Prevention
- Make guards return explicit booleans; avoid undefined/null/false-by-accident allow paths.
- Ensure the client sends the auth metadata the guard reads (gRPC metadata, RMQ/MQTT headers).
- Unit-test guards with representative contexts; assert canActivate() returns true for allowed callers.
When it happens
Trigger: An @MessagePattern/@GrpcMethod handler guarded by @UseGuards where a guard's canActivate() returns false (e.g. role check failed, JWT missing or invalid, tenant mismatch). A guard that throws is different — throwing inside a guard surfaces a different error; this specific 'Forbidden resource' is the clean false-return path.
Common situations: Authorization guard rejects the call (missing/invalid token, insufficient role). Tenant-scoped guard where the request metadata does not match the resource's tenant. Misconfigured guard that always returns false during testing or because of a bug. Custom guards that return undefined/null instead of a boolean get coerced to falsy and trigger this.
Related errors
- Invalid exception filters (@UseFilters()).
- Unauthorized
- Invalid gRPC configuration. Both protoPath and packageDefini
- Invalid gRPC configuration. protoPath or packageDefinition m
- 3
AI-assisted analysis of nestjs/nest@6ec0e2783d (2026-08-03).
Data as JSON: /data/errors/72e934b70b12743c.json.
Report an issue: GitHub.