{"record":{"id":"4eaf20ecfc5c0eb4","repo":"calcom/cal.diy","slug":"invalid-key-hours-expected-value-between-0-and","errorCode":null,"errorMessage":"Invalid ${key} hours. Expected value between 0 and 23","messagePattern":"Invalid (.+?) hours\\. Expected value between 0 and 23","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"apps/api/v2/src/platform/schedules/schedules_2024_04_15/inputs/create-availability.input.ts","lineNumber":46,"sourceCode":"  const dateTimeParts = value.split(\"T\");\n  if (dateTimeParts.length !== 2) {\n    throw new BadRequestException(\n      `Invalid datestring format. Expected format(ISO8061): 2025-04-12T13:17:56.324Z. Received: ${value}`\n    );\n  }\n\n  const timePart = dateTimeParts[1].split(\".\")[0]; // Removes milliseconds\n  const parts = timePart.split(\":\");\n\n  if (parts.length !== 3) {\n    throw new BadRequestException(\n      `Invalid time format. Expected format(ISO8061): 2025-0412T13:17:56.324Z. Received: ${value}`\n    );\n  }\n  const [hours, minutes, seconds] = parts.map(Number);\n\n  if (hours < 0 || hours > 23) {\n    throw new BadRequestException(`Invalid ${key} hours. Expected value between 0 and 23`);\n  }\n\n  if (minutes < 0 || minutes > 59) {\n    throw new BadRequestException(`Invalid ${key} minutes. Expected value between 0 and 59`);\n  }\n\n  if (seconds < 0 || seconds > 59) {\n    throw new BadRequestException(`Invalid ${key} seconds. Expected value between 0 and 59`);\n  }\n\n  return new Date(new Date().setUTCHours(hours, minutes, seconds, 0));\n}\n","sourceCodeStart":28,"sourceCodeEnd":59,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/schedules/schedules_2024_04_15/inputs/create-availability.input.ts#L28-L59","documentation":"Thrown by transformStringToDate when the parsed hours value from the time portion falls outside the valid 0–23 range. The hours component is extracted from the first colon-separated segment of the time portion and converted via Number(). Values like 24, -1, or NaN-producing strings trigger HTTP 400. The ${key} interpolation identifies which field (startTime or endTime) had the invalid hours.","triggerScenarios":"Sending a time like '24:00:00' (hours = 24); sending '-01:00:00'; sending a non-numeric hours token like 'ab:00:00' which Number() converts to NaN (NaN < 0 is false but NaN > 23 is also false, so NaN may actually slip through — this is a latent bug).","commonSituations":"Client-side arithmetic produces an hours value ≥ 24 after timezone conversion; 12-hour clock formatting without AM/PM conversion sends hours like 13+ incorrectly; manual time string construction with an off-by-one error.","solutions":["Verify the hours value in the time string is between 0 and 23 (24-hour format).","Use a Date object and toISOString() to guarantee valid hour values from the start.","Add a client-side assertion: if (hours < 0 || hours > 23) throw before sending."],"exampleFix":"// before\n\"startTime\": \"2025-04-12T24:00:00.000Z\"\n// after\n\"startTime\": new Date(2025, 3, 12, 0, 0, 0).toISOString()","handlingStrategy":"validation","validationCode":"function validateHours(isoString: string, fieldName: string): void {\n  const timePart = isoString.split('T')[1]?.split('.')[0];\n  const hours = Number(timePart?.split(':')[0]);\n  if (isNaN(hours) || hours < 0 || hours > 23) {\n    throw new Error(`${fieldName}: hours must be 0-23, got ${hours}`);\n  }\n}\n\nvalidateHours(item.startTime, 'startTime');","typeGuard":"function hasValidHours(isoString: string): boolean {\n  const timePart = isoString.split('T')[1]?.split('.')[0];\n  if (!timePart) return false;\n  const hours = Number(timePart.split(':')[0]);\n  return !isNaN(hours) && hours >= 0 && hours <= 23;\n}","tryCatchPattern":null,"preventionTips":["Use Date objects and toISOString() to produce valid hour values automatically.","Never perform hour arithmetic that can exceed 23 without using modular overflow.","Validate the hours component client-side before sending the request.","Be careful with timezone conversions that may push hours past 23."],"tags":["validation","time","range-check","input","schedules"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}