{"record":{"id":"3a2c1689fe5334ad","repo":"calcom/cal.diy","slug":"invalid-time-format-expected-format-iso8061-202","errorCode":null,"errorMessage":"Invalid time format. Expected format(ISO8061): 2025-0412T13:17:56.324Z. Received: ${value}","messagePattern":"Invalid time format\\. Expected format\\(ISO8061\\): 2025-0412T13:17:56\\.324Z\\. Received: (.+?)","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":39,"sourceCode":"function transformStringToDate(value: string, key: string): Date {\n  if (!value) {\n    throw new BadRequestException(\n      `Missing ${key}. Expected value is in ISO8061 format e.g. 2025-0412T13:17:56.324Z`\n    );\n  }\n\n  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));","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/schedules/schedules_2024_04_15/inputs/create-availability.input.ts#L21-L57","documentation":"Thrown by transformStringToDate when the time portion after the 'T' separator cannot be split into exactly three colon-separated parts (hours, minutes, seconds). The code strips milliseconds first, then splits on ':' and requires exactly 3 segments. A time like '09:00' (only 2 parts) or '09' (1 part) will trigger HTTP 400.","triggerScenarios":"Sending a time portion of '09:00' without seconds (only 2 colon-separated parts); sending '0900' with no colons at all; sending '9' as the time portion; sending a time with timezone offset like '09:00:00+02:00' which produces more than 3 parts after splitting.","commonSituations":"Most time pickers and libraries output 'HH:mm' without seconds; client uses a simplified format not matching the strict 3-part requirement; timezone offset appended to the time portion breaks the split.","solutions":["Ensure the time portion includes hours, minutes, AND seconds: '2025-04-12T09:00:00.000Z'.","If your client only has hours and minutes, append ':00' for seconds before sending: startTime + ':00'.","Migrate to the 2024-06-11 schedule API which uses simpler 'hh:mm' format without seconds."],"exampleFix":"// before\n\"startTime\": \"2025-04-12T09:00.000Z\"\n// after\n\"startTime\": \"2025-04-12T09:00:00.000Z\"","handlingStrategy":"validation","validationCode":"function validateTimeString(value: string, fieldName: string): void {\n  const parts = value.split('T');\n  if (parts.length !== 2) throw new Error(`${fieldName}: missing T separator`);\n  const timePart = parts[1].split('.')[0]; // strip ms\n  const segments = timePart.split(':');\n  if (segments.length !== 3) {\n    throw new Error(`${fieldName}: time must be hh:mm:ss, got ${timePart}`);\n  }\n}\n\nvalidateTimeString(item.startTime, 'startTime');\nvalidateTimeString(item.endTime, 'endTime');","typeGuard":"function hasThreePartTime(isoString: string): boolean {\n  const tIndex = isoString.indexOf('T');\n  if (tIndex === -1) return false;\n  const timePart = isoString.slice(tIndex + 1).split('.')[0];\n  return timePart.split(':').length === 3;\n}","tryCatchPattern":"try {\n  await api.post('/v2/schedules', payload);\n} catch (error) {\n  if (error.response?.status === 400 && error.response.data.message?.includes('Invalid time format')) {\n    // The time portion needs hh:mm:ss — append ':00' if seconds are missing\n    console.error('Fix time format to include seconds:', error.response.data.message);\n  }\n  throw error;\n}","preventionTips":["Always use toISOString() which produces hh:mm:ss.ssZ format with all three time components.","If your data source only provides hh:mm, append ':00' for seconds before sending.","Migrate to the 2024-06-11 API version which accepts 'hh:mm' without seconds.","Validate with regex: /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/."],"tags":["validation","time-format","input","schedules","iso8601"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}