{"record":{"id":"2da7cade56a5c94b","repo":"calcom/cal.diy","slug":"missing-key-expected-value-is-in-iso8061-forma","errorCode":null,"errorMessage":"Missing ${key}. Expected value is in ISO8061 format e.g. 2025-0412T13:17:56.324Z","messagePattern":"Missing (.+?)\\. Expected value is in ISO8061 format e\\.g\\. 2025-0412T13:17:56\\.324Z","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":23,"sourceCode":"\nexport class CreateAvailabilityInput_2024_04_15 {\n  @IsArray()\n  @IsNumber({}, { each: true })\n  @ApiProperty({ example: [1, 2] })\n  days!: number[];\n\n  @IsDate()\n  @Transform(({ value, key }: TransformFnParams) => transformStringToDate(value, key))\n  startTime!: Date;\n\n  @IsDate()\n  @Transform(({ value, key }: TransformFnParams) => transformStringToDate(value, key))\n  endTime!: Date;\n}\n\nfunction 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    );","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/schedules/schedules_2024_04_15/inputs/create-availability.input.ts#L5-L41","documentation":"Thrown by the class-transformer @Transform pipe on CreateAvailabilityInput_2024_04_15 when the startTime or endTime field is absent or falsy. The transformStringToDate helper runs before class-validator's @IsDate check, so a missing field never reaches validation — it fails immediately with HTTP 400. The error message embeds the property name (startTime or endTime) via the ${key} interpolation.","triggerScenarios":"POST /v2/schedules (2024-04-15 version) with an availability object that omits startTime or endTime; sending an empty string or null for either time field; sending a request body where the availability array items are missing the time keys entirely.","commonSituations":"Client SDK or frontend form doesn't include time fields when constructing the availability payload; schema mismatch after upgrading to the 2024-04-15 API version which requires startTime/endTime on every availability entry; JSON serialization drops undefined fields.","solutions":["Ensure every availability object in the request body includes both startTime and endTime as ISO 8601 strings (e.g. \"2025-04-12T13:17:56.324Z\").","Validate the request body shape before sending it — confirm startTime and endTime are non-empty strings.","Upgrade to the 2024-06-11 schedule API version which accepts simpler 'hh:mm' time strings instead of full ISO timestamps."],"exampleFix":"// before\n{ \"days\": [1,2,3,4,5], \"endTime\": \"2025-04-12T17:00:00.000Z\" }\n// after\n{ \"days\": [1,2,3,4,5], \"startTime\": \"2025-04-12T09:00:00.000Z\", \"endTime\": \"2025-04-12T17:00:00.000Z\" }","handlingStrategy":"validation","validationCode":"function validateAvailabilityInput(availability: { days: number[]; startTime?: string; endTime?: string }): string[] {\n  const errors: string[] = [];\n  if (!availability.startTime) errors.push('startTime is required');\n  if (!availability.endTime) errors.push('endTime is required');\n  return errors;\n}\n\n// Run before POST /v2/schedules (2024-04-15)\nconst errs = validateAvailabilityInput(item);\nif (errs.length) throw new Error(errs.join(', '));","typeGuard":"function isValidAvailabilityInput(value: unknown): value is { days: number[]; startTime: string; endTime: string } {\n  if (typeof value !== 'object' || value === null) return false;\n  const v = value as Record<string, unknown>;\n  return typeof v.startTime === 'string' && v.startTime.length > 0 &&\n         typeof v.endTime === 'string' && v.endTime.length > 0 &&\n         Array.isArray(v.days);\n}","tryCatchPattern":"try {\n  await api.post('/v2/schedules', { availability: [item] });\n} catch (error) {\n  if (error.response?.status === 400 && error.response.data.message?.includes('Missing')) {\n    // Add the missing startTime or endTime field and retry\n    throw new Error(`Missing required field: ${error.response.data.message}`);\n  }\n  throw error;\n}","preventionTips":["Always include both startTime and endTime in every availability object.","Use TypeScript types from @calcom/platform-types to catch missing fields at compile time.","Run a JSON-schema validation step on the request body before sending.","Consider migrating to the 2024-06-11 API which uses simpler 'hh:mm' time strings."],"tags":["validation","date","input","schedules","nestjs","class-transformer"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}