larksuite/cli · error

set_calendar: event_end must be after event_start

Error message

set_calendar: event_end must be after event_start

What it means

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.

Source

Thrown at shortcuts/mail/draft/model.go:362

	case "remove_signature":
		// No required fields.
	case "set_calendar":
		if strings.TrimSpace(op.EventSummary) == "" {
			return fmt.Errorf("set_calendar requires event_summary")
		}
		if strings.TrimSpace(op.EventStart) == "" || strings.TrimSpace(op.EventEnd) == "" {
			return fmt.Errorf("set_calendar requires event_start and event_end")
		}
		start, err := parseISO8601(op.EventStart)
		if err != nil {
			return fmt.Errorf("set_calendar: event_start must be a valid ISO 8601 timestamp")
		}
		end, err := parseISO8601(op.EventEnd)
		if err != nil {
			return fmt.Errorf("set_calendar: event_end must be a valid ISO 8601 timestamp")
		}
		if !end.After(start) {
			return fmt.Errorf("set_calendar: event_end must be after event_start")
		}
	case "remove_calendar":
		// No required fields.
	default:
		return fmt.Errorf("unsupported op %q", op.Op)
	}
	return nil
}

func isRecipientField(field string) bool {
	switch strings.ToLower(strings.TrimSpace(field)) {
	case "to", "cc", "bcc":
		return true
	default:
		return false
	}
}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Ensure event_end > event_start as absolute instants, e.g. end = start.Add(30 * time.Minute)
  2. Check for swapped arguments — if your values look reversed, exchange them
  3. 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
  4. If a zero-length event is intended, extend it to a minimal duration (e.g. 1 minute) since the parser rejects equality

Example fix

// before
start := "2026-09-04T10:00:00+08:00"; end := "2026-09-04T02:00:00Z" // same instant -> rejected
// after
end := startTime.Add(30 * time.Minute).Format(time.RFC3339)
Defensive patterns

Strategy: validation

Validate before calling

start, err := time.Parse(time.RFC3339, op.EventStart)
end, err2 := time.Parse(time.RFC3339, op.EventEnd)
if err != nil || err2 != nil || !end.After(start) {
    return errors.New("event_end must be a later instant than event_start")
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/7cf76d2c3ead2003. Report an issue: GitHub.