{"record":{"id":"5215ba70194c80b2","repo":"jdx/mise","slug":"expected-programarguments-array-got-value","errorCode":null,"errorMessage":"expected ProgramArguments array, got {value:?}","messagePattern":"expected ProgramArguments array, got (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/launchd.rs","lineNumber":809,"sourceCode":"            ))\n        );\n        assert_eq!(\n            dict.get(\"StandardErrorPath\"),\n            Some(&Value::String(\n                crate::dirs::HOME\n                    .join(\"Library\")\n                    .join(\"Logs\")\n                    .join(\"sync.err.log\")\n                    .to_string_lossy()\n                    .to_string()\n            ))\n        );\n        match dict.get(\"ProgramArguments\") {\n            Some(Value::Array(args)) => {\n                assert_eq!(args[0], Value::String(\"/bin/echo\".to_string()));\n                assert_eq!(args[1], Value::String(\"hello\".to_string()));\n            }\n            value => panic!(\"expected ProgramArguments array, got {value:?}\"),\n        }\n        match dict.get(\"EnvironmentVariables\") {\n            Some(Value::Dictionary(env)) => {\n                assert_eq!(\n                    env.get(\"PATH\"),\n                    Some(&Value::String(\"/usr/bin:/bin\".to_string()))\n                );\n            }\n            value => panic!(\"expected EnvironmentVariables dictionary, got {value:?}\"),\n        }\n        assert!(plist_matches(&plist, &request));\n    }\n\n    #[test]\n    fn test_render_plist_multiple_calendar_intervals() {\n        let request = LaunchdRequest {\n            name: \"sync\".to_string(),\n            label: \"dev.mise.sync\".to_string(),","sourceCodeStart":791,"sourceCodeEnd":827,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/launchd.rs#L791-L827","documentation":"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 {:?}.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run the test and inspect the {value:?} in the panic message to see what render_plist actually produced","Read render_plist in src/system/launchd.rs and confirm it writes the ProgramArguments array for non-kickstart requests","If the emitted key name changed, update either render_plist or this test so both use 'ProgramArguments'","If the parse shape changed (e.g. values are now a different plist Value variant), update the match patterns and assertions accordingly"],"exampleFix":"// before\nvalue => panic!(\"expected ProgramArguments array, got {value:?}\"),\n// after\n// fix render_plist so it emits:\n// \"ProgramArguments\" => Value::Array(vec![Value::String(\"/bin/echo\".into()), Value::String(\"hello\".into())])\n// then the match arm Some(Value::Array(args)) succeeds and the panic arm is unreachable","handlingStrategy":"validation","validationCode":"// in tests: assert the shape before matching\nlet dict = match Value::from_reader_xml(Cursor::new(plist.as_slice())).unwrap() {\n    Value::Dictionary(d) => d,\n    v => panic!(\"plist root not a dictionary: {v:?}\"),\n};\nassert!(matches!(dict.get(\"ProgramArguments\"), Some(Value::Array(_))),\n        \"missing/invalid ProgramArguments: {:?}\", dict.get(\"ProgramArguments\"));","typeGuard":"fn is_string_array(v: Option<&Value>) -> bool {\n    matches!(v, Some(Value::Array(items)) if items.iter().all(|i| matches!(i, Value::String(_))))\n}","tryCatchPattern":null,"preventionTips":["Keep a single serializer function for plist keys and add tests next to it","Snapshot the rendered plist so key renames surface in review","Assert on expected keys before deep-matching so failures point at the right key"],"tags":["rust","test-assertion","plist","launchd"],"backgroundTag":"unexpected-response-shape","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}