{"record":{"id":"0b3491bf03b7d907","repo":"calcom/cal.diy","slug":"apikeysservice-cannot-set-both-apikeydaysvalid-an","errorCode":null,"errorMessage":"ApiKeysService -Cannot set both apiKeyDaysValid and apiKeyNeverExpires. It has to be either or none of them.","messagePattern":"ApiKeysService -Cannot set both apiKeyDaysValid and apiKeyNeverExpires\\. It has to be either or none of them\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/api-keys/services/api-keys.service.ts","lineNumber":35,"sourceCode":"    private readonly config: ConfigService\n  ) {}\n\n  async getRequestApiKey(request: ApiAuthGuardRequest) {\n    if (request.authMethod !== AuthMethods.API_KEY) {\n      throw new UnauthorizedException(\n        \"ApiKeysService - This endpoint can only be accessed using an API key by providing 'Authorization: Bearer <apiKey>' header\"\n      );\n    }\n    const apiKey = request.get(\"Authorization\")?.replace(\"Bearer \", \"\");\n    if (!apiKey) {\n      throw new UnauthorizedException(\"ApiKeysService - No API key provided\");\n    }\n    return apiKey;\n  }\n\n  async createApiKey(authUserId: number, createApiKeyInput: CreateApiKeyInput) {\n    if (createApiKeyInput.apiKeyDaysValid && createApiKeyInput.apiKeyNeverExpires) {\n      throw new BadRequestException(\n        \"ApiKeysService -Cannot set both apiKeyDaysValid and apiKeyNeverExpires. It has to be either or none of them.\"\n      );\n    }\n\n    const defaultApiKeyDaysValid = 30;\n    const apiKeyExpiresAfterDays = createApiKeyInput.apiKeyDaysValid\n      ? createApiKeyInput.apiKeyDaysValid\n      : defaultApiKeyDaysValid;\n    const apiKeyExpiresAt = DateTime.utc().plus({ days: apiKeyExpiresAfterDays }).toJSDate();\n    const apiKey = await createApiKeyHandler({\n      ctx: {\n        user: {\n          id: authUserId,\n        },\n      },\n      input: {\n        note: createApiKeyInput.note,\n        neverExpires: !!createApiKeyInput.apiKeyNeverExpires,","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/api-keys/services/api-keys.service.ts#L17-L53","documentation":"Thrown by ApiKeysService.createApiKey when both createApiKeyInput.apiKeyDaysValid and createApiKeyInput.apiKeyNeverExpires are truthy simultaneously. The API enforces mutual exclusivity: a key either expires after a specified number of days or never expires. Setting both is logically contradictory and rejected with BadRequestException (HTTP 400) before any database operation occurs.","triggerScenarios":"A POST /v2/api-keys request body includes both { \"apiKeyDaysValid\": 90, \"apiKeyNeverExpires\": true }. A client form that allows selecting both options without disabling the other. A payload constructed programmatically where both fields default to truthy values.","commonSituations":"Frontend form validation not enforcing mutual exclusivity before submission. API client SDK with optional fields where the caller sets both inadvertently. Copy-pasting a request body and forgetting to remove one field. Default values in a test fixture that set both fields.","solutions":["Remove one of the two fields from the request body: use either apiKeyDaysValid OR apiKeyNeverExpires, never both.","Add client-side validation to disable or clear one field when the other is set.","If neither is provided, the service defaults to 30 days expiry (defaultApiKeyDaysValid at line 40)."],"exampleFix":"// before: both flags set\nconst body = {\n  note: 'CI key',\n  apiKeyDaysValid: 90,\n  apiKeyNeverExpires: true\n};\n\n// after: mutually exclusive\nconst body = {\n  note: 'CI key',\n  apiKeyDaysValid: 90\n  // OR: apiKeyNeverExpires: true\n};","handlingStrategy":"validation","validationCode":"// Enforce mutual exclusivity before making the request\nconst validateCreateApiKeyInput = (input: {\n  apiKeyDaysValid?: number;\n  apiKeyNeverExpires?: boolean;\n}): void => {\n  if (input.apiKeyDaysValid && input.apiKeyNeverExpires) {\n    throw new Error(\n      'Cannot set both apiKeyDaysValid and apiKeyNeverExpires. Choose one or neither (defaults to 30 days).'\n    );\n  }\n  if (input.apiKeyDaysValid !== undefined && input.apiKeyDaysValid <= 0) {\n    throw new Error('apiKeyDaysValid must be a positive number');\n  }\n};","typeGuard":"type ValidCreateApiKeyInput =\n  | { apiKeyDaysValid: number; apiKeyNeverExpires?: false }\n  | { apiKeyDaysValid?: number; apiKeyNeverExpires: true }\n  | { apiKeyDaysValid?: undefined; apiKeyNeverExpires?: undefined };\n\nconst isMutuallyExclusive = (i: {\n  apiKeyDaysValid?: number;\n  apiKeyNeverExpires?: boolean;\n}): i is ValidCreateApiKeyInput =>\n  !(i.apiKeyDaysValid && i.apiKeyNeverExpires);","tryCatchPattern":null,"preventionTips":["In frontend forms, disable the 'never expires' toggle when a days-valid value is entered, and vice versa.","In API client SDKs, use a discriminated union type for the input to make setting both fields a compile-time error.","Add a pre-request validation layer in your API client that checks mutual exclusivity before sending."],"tags":["validation","api-key","nestjs","api-v2","bad-request","input-validation"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}