jdx/mise · error

expected StartCalendarInterval array, got {value:?}

Error message

expected StartCalendarInterval array, got {value:?}

What it means

Test assertion panic in test_render_plist_multiple_calendar_intervals. When a request carries two calendar schedules, the rendered plist must expose them as an array under 'StartCalendarInterval'; if the key is missing or holds a single dictionary/other type, this panic fires with the actual value.

Source

Thrown at src/system/launchd.rs:878

        match dict.get("StartCalendarInterval") {
            Some(Value::Array(intervals)) => {
                assert_eq!(intervals.len(), 2);
                match &intervals[0] {
                    Value::Dictionary(interval) => {
                        assert_eq!(interval.get("Hour"), Some(&Value::Integer(3.into())));
                        assert_eq!(interval.get("Minute"), Some(&Value::Integer(0.into())));
                    }
                    value => panic!("expected first calendar interval dictionary, got {value:?}"),
                }
                match &intervals[1] {
                    Value::Dictionary(interval) => {
                        assert_eq!(interval.get("Hour"), Some(&Value::Integer(12.into())));
                        assert_eq!(interval.get("Weekday"), Some(&Value::Integer(1.into())));
                    }
                    value => panic!("expected second calendar interval dictionary, got {value:?}"),
                }
            }
            value => panic!("expected StartCalendarInterval array, got {value:?}"),
        }
        assert_eq!(dict.get("ThrottleInterval"), None);
        assert_eq!(dict.get("QueueDirectories"), None);
    }

    #[test]
    fn test_render_plist_throttle_and_queue_directories() {
        let request = LaunchdRequest::from_toml(
            "sync".to_string(),
            LaunchdTomlConfig {
                program: Some("/bin/echo".to_string()),
                throttle_interval: Some(10),
                nice: None,
                queue_directories: vec![
                    "~/Library/Queues/sync".to_string(),
                    "/var/spool/sync".to_string(),
                ],
                ..Default::default()

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect the {value:?} payload to see what render_plist emitted for StartCalendarInterval
  2. Make render_plist emit an array of interval dictionaries when more than one schedule exists (launchd accepts a single dict only for one schedule)
  3. Re-run the test to confirm both intervals appear with correct Hour/Minute/Weekday keys

Example fix

// before
"StartCalendarInterval" => Value::Dictionary(interval)
// after
"StartCalendarInterval" => Value::Array(intervals) // when request.calendar_intervals.len() > 1
Defensive patterns

Strategy: validation

Validate before calling

match dict.get("StartCalendarInterval") {
    Some(Value::Array(a)) if a.len() > 1 => {},
    Some(Value::Dictionary(_)) | None => panic!(
        "multi-schedule request must render StartCalendarInterval as array, got {:?}",
        dict.get("StartCalendarInterval")
    ),
    other => panic!("unexpected StartCalendarInterval: {other:?}"),
}

Type guard

fn is_multi_interval(v: Option<&Value>) -> bool {
    matches!(v, Some(Value::Array(a)) if a.len() > 1)
}

Prevention

When it happens

Trigger: render_plist serializes a two-interval request as a single StartCalendarInterval dictionary instead of an array (or omits the key), so dict.get("StartCalendarInterval") is not Some(Value::Array(_)).

Common situations: A refactor that always writes StartCalendarInterval as a bare dictionary (valid for a single schedule but wrong for multiple), regressing launchd's multi-interval array form.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/d1484c7d33a03966. Report an issue: GitHub.