jdx/mise · error

expected ProgramArguments array, got {value:?}

Error message

expected ProgramArguments array, got {value:?}

What it means

This is a test assertion panic in test_render_plist. After render_plist produces a launchd plist XML document, the test parses it with the plist crate and requires the top-level value to be a dictionary whose 'ProgramArguments' key holds an array with /bin/echo as argv[0] and hello as argv[1]. The panic fires in the catch-all match arm whenever the parsed value does not match, printing the offending value with {:?}.

Source

Thrown at src/system/launchd.rs:809

            ))
        );
        assert_eq!(
            dict.get("StandardErrorPath"),
            Some(&Value::String(
                crate::dirs::HOME
                    .join("Library")
                    .join("Logs")
                    .join("sync.err.log")
                    .to_string_lossy()
                    .to_string()
            ))
        );
        match dict.get("ProgramArguments") {
            Some(Value::Array(args)) => {
                assert_eq!(args[0], Value::String("/bin/echo".to_string()));
                assert_eq!(args[1], Value::String("hello".to_string()));
            }
            value => panic!("expected ProgramArguments array, got {value:?}"),
        }
        match dict.get("EnvironmentVariables") {
            Some(Value::Dictionary(env)) => {
                assert_eq!(
                    env.get("PATH"),
                    Some(&Value::String("/usr/bin:/bin".to_string()))
                );
            }
            value => panic!("expected EnvironmentVariables dictionary, got {value:?}"),
        }
        assert!(plist_matches(&plist, &request));
    }

    #[test]
    fn test_render_plist_multiple_calendar_intervals() {
        let request = LaunchdRequest {
            name: "sync".to_string(),
            label: "dev.mise.sync".to_string(),

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Run the test and inspect the {value:?} in the panic message to see what render_plist actually produced
  2. Read render_plist in src/system/launchd.rs and confirm it writes the ProgramArguments array for non-kickstart requests
  3. If the emitted key name changed, update either render_plist or this test so both use 'ProgramArguments'
  4. If the parse shape changed (e.g. values are now a different plist Value variant), update the match patterns and assertions accordingly

Example fix

// before
value => panic!("expected ProgramArguments array, got {value:?}"),
// after
// fix render_plist so it emits:
// "ProgramArguments" => Value::Array(vec![Value::String("/bin/echo".into()), Value::String("hello".into())])
// then the match arm Some(Value::Array(args)) succeeds and the panic arm is unreachable
Defensive patterns

Strategy: validation

Validate before calling

// in tests: assert the shape before matching
let dict = match Value::from_reader_xml(Cursor::new(plist.as_slice())).unwrap() {
    Value::Dictionary(d) => d,
    v => panic!("plist root not a dictionary: {v:?}"),
};
assert!(matches!(dict.get("ProgramArguments"), Some(Value::Array(_))),
        "missing/invalid ProgramArguments: {:?}", dict.get("ProgramArguments"));

Type guard

fn is_string_array(v: Option<&Value>) -> bool {
    matches!(v, Some(Value::Array(items)) if items.iter().all(|i| matches!(i, Value::String(_))))
}

Prevention

When it happens

Trigger: render_plist(&request) returns a plist that (a) fails to parse as XML, (b) parses to a non-dictionary root, or (c) has a missing or non-array 'ProgramArguments' key — i.e. render_plist stopped emitting ProgramArguments or emitted it in a different shape.

Common situations: Refactoring render_plist to rename or drop ProgramArguments, changing the Value enum used for parsed plists, switching the plist parsing crate or format (binary vs XML), or a partial edit that emits Program keys only for kickstart-style jobs.

Related errors


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