calcom/cal.diy · critical · ForbiddenException
No ooo entry id found in request params.
Error message
No ooo entry id found in request params.
What it means
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.
Source
Thrown at apps/api/v2/src/modules/ooo/guards/is-user-ooo.ts:19
import { UserOOORepository } from "@/modules/ooo/repositories/ooo.repository";
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Request } from "express";
@Injectable()
export class IsUserOOO implements CanActivate {
constructor(private oooRepo: UserOOORepository) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
const oooId: string = request.params.oooId;
const userId: string = request.params.userId;
if (!userId) {
throw new ForbiddenException("No user id found in request params.");
}
if (!oooId) {
throw new ForbiddenException("No ooo entry id found in request params.");
}
const ooo = await this.oooRepo.getUserOOOByIdAndUserId(Number(oooId), Number(userId));
if (ooo) {
return true;
}
throw new ForbiddenException("This OOO entry does not belong to this user.");
}
}
View on GitHub (pinned to 176037d0af)
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.
Example fix
// before
@Controller('users/:userId/ooo')
@UseGuards(IsUserOOO)
export class OOOController {
@Post() // no :oooId — guard throws on create
create() { ... }
}
// after — split guarded vs unguarded routes
@Controller('users/:userId/ooo')
export class OOOController {
@Post()
create() { ... }
@Patch(':oooId')
@UseGuards(IsUserOOO)
update() { ... }
} Defensive patterns
Strategy: validation
Validate before calling
// Only apply IsUserOOO to routes that act on a single OOO entry
function isSingleEntryRoute(httpMethod: string, path: string) {
return /:oooId/.test(path) && httpMethod !== 'POST';
}
if (!isSingleEntryRoute(method, path)) {
throw new Error('IsUserOOO should not guard collection/create routes');
} Type guard
function requestHasOooId(req: { params?: Record<string, unknown> }): req is { params: { oooId: string } } {
return typeof req.params?.oooId === 'string' && (req.params as any).oooId.length > 0;
} Try / catch
// Routing bug — fix the route, do not catch at runtime.
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- No user id found in request params.
- OAuthClientGuard - No OAuth client associated with the reque
- IsUserWebhookGuard - No webhook id found in request params.
- EventTypeOwnershipGuard - No user associated with the reques
- Missing eventTypeId param.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/bb5e2e904773ca69.
Report an issue: GitHub.