{"record":{"id":"6d3b67fd8c61cddc","repo":"invoke-ai/InvokeAI","slug":"call-saved-workflow-child-workflow-is-malformed","errorCode":null,"errorMessage":"call_saved_workflow child workflow is malformed","messagePattern":"call_saved_workflow child workflow is malformed","errorType":"exception","errorClass":"UnsupportedWorkflowNodeError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/session_processor/workflow_call_batch.py","lineNumber":141,"sourceCode":"        if source_node is None:\n            return None\n\n        if _is_invocation_node(source_node):\n            return (source_id, source_handle)\n\n        if _is_connector_node(source_node):\n            return resolve(source_id)\n\n        return None\n\n    return resolve(connector_id)\n\n\ndef _build_child_graph_workflow(workflow: Mapping[str, Any], used_generator_node_ids: set[str]) -> dict[str, Any]:\n    workflow_nodes = workflow.get(\"nodes\", [])\n    workflow_edges = workflow.get(\"edges\", [])\n    if not isinstance(workflow_nodes, list) or not isinstance(workflow_edges, list):\n        raise UnsupportedWorkflowNodeError(\"call_saved_workflow child workflow is malformed\")\n\n    filtered_nodes = [\n        node\n        for node in workflow_nodes\n        if not (\n            _is_invocation_node(node)\n            and (\n                node[\"data\"].get(\"type\") in SUPPORTED_BATCH_TYPES\n                or (isinstance(node.get(\"id\"), str) and node[\"id\"] in used_generator_node_ids)\n            )\n        )\n    ]\n    filtered_node_ids = {node[\"id\"] for node in filtered_nodes if _is_mapping(node) and isinstance(node.get(\"id\"), str)}\n    filtered_edges = [\n        edge\n        for edge in workflow_edges\n        if _is_mapping(edge)\n        and edge.get(\"type\") == \"default\"","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/session_processor/workflow_call_batch.py#L123-L159","documentation":"_build_child_graph_workflow filters a saved workflow before expanding a call_saved_workflow node into a batch of child sessions. It requires the workflow's 'nodes' and 'edges' members to both be lists; if either is missing or of the wrong JSON type it raises UnsupportedWorkflowNodeError('call_saved_workflow child workflow is malformed'), since the expansion cannot proceed on a structurally invalid workflow.","triggerScenarios":"Executing a batch whose call_saved_workflow node references a saved workflow whose stored JSON has nodes/edges absent, null, dicts, or otherwise not arrays — e.g. a hand-edited or partially migrated workflow record in the workflow library.","commonSituations":"Workflows authored by older InvokeAI versions or third-party tools with a different schema; corrupted rows after a database import/export; users pasting workflow JSON that got mangled (nodes as an object keyed by id instead of a list).","solutions":["Open the saved workflow in the InvokeAI workflow editor and re-save it so nodes/edges are serialized in the current list-based schema.","Inspect the stored workflow JSON (workflow library/DB) and fix or remove the malformed record.","Update the workflow from the version it was authored in before using it in a call_saved_workflow batch node.","Catch UnsupportedWorkflowNodeError around batch building and skip/report the offending workflow."],"exampleFix":"// before\nworkflow = {\"id\": \"w1\", \"nodes\": {\"n1\": {...}}, \"edges\": []}  # nodes as dict\n// after\nworkflow = {\"id\": \"w1\", \"nodes\": [{\"id\": \"n1\", ...}], \"edges\": []}  # lists","handlingStrategy":"type-guard","validationCode":"def child_workflow_is_wellformed(workflow: dict) -> bool:\n    return isinstance(workflow.get(\"nodes\"), list) and isinstance(workflow.get(\"edges\"), list)\n\nif not child_workflow_is_wellformed(saved_workflow):\n    raise ValueError(\"saved workflow must have list-typed nodes and edges\")","typeGuard":"def is_list_workflow(w: object) -> bool:\n    return isinstance(w, dict) and isinstance(w.get(\"nodes\"), list) and isinstance(w.get(\"edges\"), list)","tryCatchPattern":"try:\n    results = build_batch_child_workflow_session_results(...)\nexcept UnsupportedWorkflowNodeError:\n    results = []  # skip batch; surface workflow as invalid in UI","preventionTips":["Re-save old/external workflows in the current workflow editor before use in batch calls","Validate workflow JSON schema on import/export","Do not hand-edit stored workflow records in the DB","Verify nodes/edges are arrays when migrating workflows between versions"],"tags":["invokeai","workflow","batch","schema-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}