jdx/mise · error
expected StartCalendarInterval dictionary, got {value:?}
Error message
expected StartCalendarInterval dictionary, got {value:?} What it means
A companion panic in `test_render_plist`: after confirming the plist root is a dictionary, the test looks up the `StartCalendarInterval` key and asserts it is a `Value::Dictionary` containing Hour=2 and Minute=0. If the key is missing or holds another value type, the catch-all panics with 'expected StartCalendarInterval dictionary, got {value:?}'.
Source
Thrown at src/system/launchd.rs:770
Some(&Value::Integer(300.into()))
);
assert_eq!(
dict.get("QueueDirectories"),
Some(&Value::Array(vec![Value::String(
crate::dirs::HOME
.join("Library")
.join("Queues")
.join("sync")
.to_string_lossy()
.to_string()
)]))
);
match dict.get("StartCalendarInterval") {
Some(Value::Dictionary(interval)) => {
assert_eq!(interval.get("Hour"), Some(&Value::Integer(2.into())));
assert_eq!(interval.get("Minute"), Some(&Value::Integer(0.into())));
}
value => panic!("expected StartCalendarInterval dictionary, got {value:?}"),
}
assert_eq!(
dict.get("WorkingDirectory"),
Some(&Value::String(
crate::dirs::HOME.to_string_lossy().to_string()
))
);
// joined component-by-component to match how `file::replace_path`
// expands the `~/...` request values: the rendered string uses the
// platform separator throughout, so `join("Library/Logs/sync.log")`
// would keep forward slashes and mismatch on Windows
assert_eq!(
dict.get("StandardOutPath"),
Some(&Value::String(
crate::dirs::HOME
.join("Library")
.join("Logs")
.join("sync.log")View on GitHub (pinned to afd2eddd3a)
Solutions
- Verify `render_plist` emits `StartCalendarInterval` as a nested `<dict>` whenever the request has a schedule.
- Check that Hour/Minute values from the request are mapped into the nested dictionary, not top-level keys.
- Print the whole parsed dictionary to see which keys were actually emitted.
- Ensure optional schedule fields with defaults (e.g. Minute=0) are not treated as 'unset' and skipped.
Example fix
// before
value => panic!("expected StartCalendarInterval dictionary, got {value:?}"),
// after
value => panic!("expected StartCalendarInterval dictionary, got {value:?}; keys: {:?}", dict.keys().collect::<Vec<_>>()), Defensive patterns
Strategy: type-guard
Validate before calling
// rust
if !dict.contains_key("StartCalendarInterval") {
return Err("plist missing StartCalendarInterval".into());
} Type guard
fn calendar_interval(dict: &Dictionary) -> Option<&Dictionary> {
match dict.get("StartCalendarInterval") {
Some(Value::Dictionary(d)) => Some(d),
_ => None,
}
} Try / catch
// rust
match dict.get("StartCalendarInterval") {
Some(Value::Dictionary(interval)) => /* assert Hour/Minute */,
other => return Err(anyhow!("expected StartCalendarInterval dictionary, got {other:?}")),
} Prevention
- Test every optional launchd key (StartCalendarInterval, WorkingDirectory) individually.
- Ensure schedule defaults (Minute=0) still emit the key.
- Keep key names centralized to avoid typos/renames.
- Assert nested dictionary structure, not just key presence.
When it happens
Trigger: `render_plist` emitting no `StartCalendarInterval` key (None case) or emitting it as a non-dictionary (e.g. string) for a request with schedule settings — a mapping regression between the request's schedule fields and the plist output.
Common situations: Schedule fields dropped from the request struct or renderer; `Hour`/`Minute` written at the top level instead of nested; key renamed in the plist output; optional schedule omitted when a field defaults.
Related errors
- expected dictionary, got {value:?}
- expected ProgramArguments array, got {value:?}
- expected EnvironmentVariables dictionary, got {value:?}
- expected first calendar interval dictionary, got {value:?}
- expected second calendar interval dictionary, got {value:?}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/935b7827f091d776.
Report an issue: GitHub.