{"record":{"id":"bb5e2e904773ca69","repo":"calcom/cal.diy","slug":"no-ooo-entry-id-found-in-request-params","errorCode":null,"errorMessage":"No ooo entry id found in request params.","messagePattern":"No ooo entry id found in request params\\.","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"critical","filePath":"apps/api/v2/src/modules/ooo/guards/is-user-ooo.ts","lineNumber":19,"sourceCode":"import { UserOOORepository } from \"@/modules/ooo/repositories/ooo.repository\";\nimport { Injectable, CanActivate, ExecutionContext, ForbiddenException } from \"@nestjs/common\";\nimport { Request } from \"express\";\n\n@Injectable()\nexport class IsUserOOO implements CanActivate {\n  constructor(private oooRepo: UserOOORepository) {}\n\n  async canActivate(context: ExecutionContext): Promise<boolean> {\n    const request = context.switchToHttp().getRequest<Request>();\n    const oooId: string = request.params.oooId;\n    const userId: string = request.params.userId;\n\n    if (!userId) {\n      throw new ForbiddenException(\"No user id found in request params.\");\n    }\n\n    if (!oooId) {\n      throw new ForbiddenException(\"No ooo entry id found in request params.\");\n    }\n\n    const ooo = await this.oooRepo.getUserOOOByIdAndUserId(Number(oooId), Number(userId));\n\n    if (ooo) {\n      return true;\n    }\n\n    throw new ForbiddenException(\"This OOO entry does not belong to this user.\");\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/ooo/guards/is-user-ooo.ts#L1-L31","documentation":"Thrown by the IsUserOOO guard when request.params.oooId is falsy. The guard expects a :oooId route param identifying the specific OOO entry; without it the ownership check cannot run. ForbiddenException (HTTP 403). As with 137, this is a server-side routing defect, not an end-user error.","triggerScenarios":"An OOO route guarded by IsUserOOO is mounted without a :oooId segment, or the request path doesn't populate the param. E.g. a list/create route inadvertently decorated with the guard.","commonSituations":"Applying IsUserOOO to a route that doesn't act on a single OOO entry (e.g. POST create); dropping :oooId during a refactor; a test omitting oooId in mocked params.","solutions":["Only apply IsUserOOO to routes that target a specific OOO entry by :oooId (GET/PATCH/DELETE one entry).","Ensure :oooId is present in the path of every guarded route.","In tests, populate request.params.oooId before invoking the guard.","Move create/list routes to a controller not guarded by IsUserOOO."],"exampleFix":"// before\n@Controller('users/:userId/ooo')\n@UseGuards(IsUserOOO)\nexport class OOOController {\n  @Post()                // no :oooId — guard throws on create\n  create() { ... }\n}\n\n// after — split guarded vs unguarded routes\n@Controller('users/:userId/ooo')\nexport class OOOController {\n  @Post()\n  create() { ... }\n\n  @Patch(':oooId')\n  @UseGuards(IsUserOOO)\n  update() { ... }\n}","handlingStrategy":"validation","validationCode":"// Only apply IsUserOOO to routes that act on a single OOO entry\nfunction isSingleEntryRoute(httpMethod: string, path: string) {\n  return /:oooId/.test(path) && httpMethod !== 'POST';\n}\nif (!isSingleEntryRoute(method, path)) {\n  throw new Error('IsUserOOO should not guard collection/create routes');\n}","typeGuard":"function requestHasOooId(req: { params?: Record<string, unknown> }): req is { params: { oooId: string } } {\n  return typeof req.params?.oooId === 'string' && (req.params as any).oooId.length > 0;\n}","tryCatchPattern":"// Routing bug — fix the route, do not catch at runtime.","preventionTips":["Do not put IsUserOOO on POST/create or list routes.","Ensure :oooId is present on every guarded PATCH/GET/DELETE route.","Mock request.params.oooId in guard unit tests."],"tags":["nestjs","guard","routing","ooo","configuration","server-bug"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}