{"id":"72e934b70b12743c","repo":"nestjs/nest","slug":"forbidden-resource-72e934","errorCode":null,"errorMessage":"Forbidden resource","messagePattern":"Forbidden resource","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"packages/microservices/context/rpc-context-creator.ts","lineNumber":162,"sourceCode":"    return Reflect.getMetadata(PARAMTYPES_METADATA, instance, callback.name);\n  }\n\n  public createGuardsFn<TContext extends string = ContextType>(\n    guards: any[],\n    instance: Controller,\n    callback: (...args: unknown[]) => unknown,\n    contextType?: TContext,\n  ): Function | null {\n    const canActivateFn = async (args: any[]) => {\n      const canActivate = await this.guardsConsumer.tryActivate<TContext>(\n        guards,\n        args,\n        instance,\n        callback,\n        contextType,\n      );\n      if (!canActivate) {\n        throw new RpcException(FORBIDDEN_MESSAGE);\n      }\n    };\n    return guards.length ? canActivateFn : null;\n  }\n\n  public getMetadata<TMetadata, TContext extends ContextType = ContextType>(\n    instance: Controller,\n    methodName: string,\n    defaultCallMetadata: Record<string, any>,\n    contextType: TContext,\n  ): RpcHandlerMetadata {\n    const cacheMetadata = this.handlerMetadataStorage.get(instance, methodName);\n    if (cacheMetadata) {\n      return cacheMetadata;\n    }\n    const metadata =\n      this.contextUtils.reflectCallbackMetadata<TMetadata>(\n        instance,","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/nestjs/nest/blob/6ec0e2783d15290732447f304d8549b591b9749e/packages/microservices/context/rpc-context-creator.ts#L144-L180","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n@Injectable()\nclass RoleGuard implements CanActivate {\n  canActivate(ctx: ExecutionContext): boolean {\n    const req = ctx.switchToRpc().getContext();\n    return req.user?.role === 'admin'; // undefined -> false -> 'Forbidden resource'\n  }\n}\n\n// after\n@Injectable()\nclass RoleGuard implements CanActivate {\n  canActivate(ctx: ExecutionContext): boolean {\n    const req = ctx.switchToRpc().getContext();\n    return Boolean(req.user && req.user.role === 'admin');\n  }\n}\n// and on the client: send the auth metadata so user is populated","handlingStrategy":"try-catch","validationCode":"// No pre-call validation possible from the caller for a guard's decision;\n// instead ensure the caller sends the credentials the guard inspects.\nfunction withAuthMetadata<T>(payload: T, token: string) {\n  return { data: payload, metadata: { authorization: `Bearer ${token}` } };\n}","typeGuard":"import { RpcException } from '@nestjs/microservices';\nconst isForbiddenRpc = (e: unknown): boolean =>\n  e instanceof RpcException && /Forbidden resource/.test(e?.message ?? '');","tryCatchPattern":"try {\n  await firstValueFrom(client.send('getUser', payload));\n} catch (e) {\n  if (isForbiddenRpc(e)) {\n    // redirect to login / refresh token / surface 403\n  } else throw e;\n}","preventionTips":["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."],"tags":["guards","authorization","rpc","nestjs","typescript"],"analyzedSha":"6ec0e2783d15290732447f304d8549b591b9749e","analyzedAt":"2026-08-03T17:42:23.673Z","schemaVersion":2}