{"record":{"id":"7cf76d2c3ead2003","repo":"larksuite/cli","slug":"set-calendar-event-end-must-be-after-event-start","errorCode":null,"errorMessage":"set_calendar: event_end must be after event_start","messagePattern":"set_calendar: event_end must be after event_start","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/mail/draft/model.go","lineNumber":362,"sourceCode":"\tcase \"remove_signature\":\n\t\t// No required fields.\n\tcase \"set_calendar\":\n\t\tif strings.TrimSpace(op.EventSummary) == \"\" {\n\t\t\treturn fmt.Errorf(\"set_calendar requires event_summary\")\n\t\t}\n\t\tif strings.TrimSpace(op.EventStart) == \"\" || strings.TrimSpace(op.EventEnd) == \"\" {\n\t\t\treturn fmt.Errorf(\"set_calendar requires event_start and event_end\")\n\t\t}\n\t\tstart, err := parseISO8601(op.EventStart)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"set_calendar: event_start must be a valid ISO 8601 timestamp\")\n\t\t}\n\t\tend, err := parseISO8601(op.EventEnd)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"set_calendar: event_end must be a valid ISO 8601 timestamp\")\n\t\t}\n\t\tif !end.After(start) {\n\t\t\treturn fmt.Errorf(\"set_calendar: event_end must be after event_start\")\n\t\t}\n\tcase \"remove_calendar\":\n\t\t// No required fields.\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported op %q\", op.Op)\n\t}\n\treturn nil\n}\n\nfunc isRecipientField(field string) bool {\n\tswitch strings.ToLower(strings.TrimSpace(field)) {\n\tcase \"to\", \"cc\", \"bcc\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/mail/draft/model.go#L344-L380","documentation":"This is a semantic validation, not a format error: both event_start and event_end parsed successfully, but the parser requires end to be strictly after start (end.After(start)). An end equal to start (zero-duration event) or before start is rejected before any API call is made.","triggerScenarios":"set_calendar op where event_end <= event_start after parsing: identical timestamps, swapped start/end values, end computed as start + 0 duration, or a negative duration applied to the end.","commonSituations":"Swapping arguments when constructing the op (end passed as start); timezone confusion where end is formatted in a different offset than start so wall-clock strings look ordered but instants are not; setting end = start for an 'instant' event; interval math producing zero or negative length.","solutions":["Ensure event_end > event_start as absolute instants, e.g. end = start.Add(30 * time.Minute)","Check for swapped arguments — if your values look reversed, exchange them","Compare parsed instants (time.Parse both values and check end.After(start)) rather than string comparison, since offsets like +08:00 vs Z change the ordering","If a zero-length event is intended, extend it to a minimal duration (e.g. 1 minute) since the parser rejects equality"],"exampleFix":"// before\nstart := \"2026-09-04T10:00:00+08:00\"; end := \"2026-09-04T02:00:00Z\" // same instant -> rejected\n// after\nend := startTime.Add(30 * time.Minute).Format(time.RFC3339)","handlingStrategy":"validation","validationCode":"start, err := time.Parse(time.RFC3339, op.EventStart)\nend, err2 := time.Parse(time.RFC3339, op.EventEnd)\nif err != nil || err2 != nil || !end.After(start) {\n    return errors.New(\"event_end must be a later instant than event_start\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute end as start.Add(duration) so ordering is guaranteed by construction","Compare parsed instants, never wall-clock strings, especially across different timezones","Guard against zero-length events — the parser rejects end == start; use a minimal duration instead","Unit-test any duration arithmetic that could produce negative or zero intervals"],"tags":["validation","calendar","draft-patch","date-range"],"backgroundTag":"invalid-date-range","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}