{"record":{"id":"412d6f95aaadee55","repo":"windmill-labs/windmill","slug":"unable-to-deserialize-group-names","errorCode":null,"errorMessage":"Unable to deserialize group names","messagePattern":"Unable to deserialize group names","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"backend/windmill-worker/src/worker_flow.rs","lineNumber":3465,"sourceCode":"\n            // Persist approval user groups conditions, if any. Requires runnning the InputTransform\n            let required_events = suspend.required_events.unwrap() as u16;\n            let user_auth_required = suspend.user_auth_required.unwrap_or(false);\n            let self_approval_disabled = suspend.self_approval_disabled.unwrap_or(false);\n            // self_approval_disabled must be persisted even without user_auth_required, otherwise\n            // the resume boundary sees no approval_conditions and the restriction is silently\n            // dropped. user_groups_required only applies together with user_auth_required.\n            if user_auth_required || self_approval_disabled {\n                let user_groups_required: Vec<String>;\n                if !user_auth_required {\n                    user_groups_required = Vec::new();\n                } else if let Some(user_groups_required_as_input_transform) =\n                    suspend.user_groups_required\n                {\n                    match user_groups_required_as_input_transform {\n                        InputTransform::Static { value } => {\n                            user_groups_required = serde_json::from_str::<Vec<String>>(value.get())\n                                .expect(\"Unable to deserialize group names\");\n                        }\n                        InputTransform::Javascript { expr } => {\n                            let mut context = HashMap::with_capacity(2);\n                            context.insert(\"result\".to_string(), arc_last_job_result.clone());\n                            context\n                                .insert(\"previous_result\".to_string(), arc_last_job_result.clone());\n\n                            let eval_result = serde_json::from_str::<Vec<String>>(\n                                 eval_timeout(\n                                     expr.to_string(),\n                                     context,\n                                     Some(arc_flow_job_args.clone()),\n                                     flow_env,\n                                     None,\n                                     None,\n                                     None\n                                 )\n                                 .warn_after_seconds(3)","sourceCodeStart":3447,"sourceCodeEnd":3483,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-worker/src/worker_flow.rs#L3447-L3483","documentation":"In Windmill's flow execution, an approval/suspend step can require specific user groups. When the requirement is provided as a static input transform, the worker parses its raw JSON string into Vec<String>. If the string is not valid JSON or not an array of strings, serde_json fails and .expect panics the flow job with \"Unable to deserialize group names\".","triggerScenarios":"A flow suspend step's 'user_groups_required' input transform is set to InputTransform::Static whose value string is not valid JSON (e.g. a bare string, single-quoted JSON, an object, or an array of non-strings).","commonSituations":"Hand-editing a flow definition YAML/JSON and writing the group list as 'admins, devs' instead of [\"admins\",\"devs\"]; exporting/importing flows between workspaces with a malformed static value; programmatic flow deploy via SDK with a wrongly-typed value.","solutions":["Fix the suspend step's user_groups_required static value to be a valid JSON array of strings, e.g. [\"admins\",\"devs\"]","Re-deploy the corrected flow (UI editor, wmill flow push, or SDK)","If the value must be dynamic, switch the input transform to a JavaScript/AI transform that evaluates to a string array"],"exampleFix":"// before (flow definition)\n\"user_groups_required\": { \"type\": \"static\", \"value\": \"admins\" }\n// after\n\"user_groups_required\": { \"type\": \"static\", \"value\": \"[\\\"admins\\\"]\" }","handlingStrategy":"validation","validationCode":"let groups: Option<Vec<String>> = serde_json::from_str(value.get()).ok();\nif groups.is_none() {\n    eprintln!(\"user_groups_required static value is not a JSON array of strings: {}\", value.get());\n}","typeGuard":"fn is_string_array(v: &serde_json::Value) -> bool {\n    v.as_array().map_or(false, |a| a.iter().all(|x| x.is_string()))\n}","tryCatchPattern":"match serde_json::from_str::<Vec<String>>(value.get()) {\n    Ok(g) => user_groups_required = g,\n    Err(e) => return Err(Error::ExecutionErr(format!(\"Invalid user_groups_required JSON: {e}\"))),\n}","preventionTips":["Always author static values as JSON arrays of strings, e.g. [\"admins\"]","Validate flow definitions on deploy with a JSON-schema check on suspend settings","Prefer the UI editor over hand-editing flow YAML/JSON"],"tags":["serde","json","flow","configuration"],"backgroundTag":"json-deserialization-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}