{"record":{"id":"6acf8b0ecfe9f18d","repo":"invoke-ai/InvokeAI","slug":"the-selected-saved-workflow-must-contain-exactly-o","errorCode":null,"errorMessage":"The selected saved workflow must contain exactly one workflow_return node.","messagePattern":"The selected saved workflow must contain exactly one workflow_return node\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/app/services/session_processor/workflow_call_runtime.py","lineNumber":143,"sourceCode":"        self._session_runner = session_runner\n\n    @staticmethod\n    def get_waiting_workflow_call_invocation(queue_item: SessionQueueItem) -> CallSavedWorkflowInvocation:\n        waiting_frame = queue_item.session.waiting_workflow_call\n        if waiting_frame is None:\n            raise ValueError(\"Execution state is not waiting on a workflow call.\")\n        invocation = queue_item.session.execution_graph.nodes.get(waiting_frame.prepared_call_node_id)\n        if not isinstance(invocation, CallSavedWorkflowInvocation):\n            raise ValueError(\"Waiting workflow call frame does not point to a call_saved_workflow invocation.\")\n        return invocation\n\n    @staticmethod\n    def get_child_workflow_return_output(child_session: GraphExecutionState) -> WorkflowReturnOutput:\n        workflow_return_node_ids = [\n            node_id for node_id, node in child_session.graph.nodes.items() if node.get_type() == \"workflow_return\"\n        ]\n        if not workflow_return_node_ids:\n            raise ValueError(\"The selected saved workflow must contain exactly one workflow_return node.\")\n        if len(workflow_return_node_ids) > 1:\n            raise ValueError(\"The selected saved workflow must not contain more than one workflow_return node.\")\n\n        workflow_return_node_id = workflow_return_node_ids[0]\n        prepared_return_node_ids = child_session.source_prepared_mapping.get(workflow_return_node_id, set())\n        if len(prepared_return_node_ids) != 1:\n            raise ValueError(\n                \"The selected saved workflow produced an unsupported number of workflow_return executions.\"\n            )\n\n        prepared_return_node_id = next(iter(prepared_return_node_ids))\n        output = child_session.results.get(prepared_return_node_id)\n        if not isinstance(output, WorkflowReturnOutput):\n            raise ValueError(\"The selected saved workflow did not produce a valid workflow_return output.\")\n\n        return output\n\n    def resume_waiting_workflow_call(self, queue_item: SessionQueueItem) -> None:","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/session_processor/workflow_call_runtime.py#L125-L161","documentation":"When resuming a parent from a completed child workflow, the runtime extracts the child's output from its workflow_return node. If the child graph has none, ValueError is raised: a called saved workflow must terminate in exactly one workflow_return node to deliver a value back to the caller.","triggerScenarios":"get_child_workflow_return_output scans child_session.graph.nodes for get_type() == 'workflow_return' and finds zero matches — i.e. the saved workflow was called but lacks a workflow_return terminal node.","commonSituations":"Saving a workflow in the editor without adding a workflow_return node before using it in call_saved_workflow; older workflows predating the workflow_return requirement; workflows truncated during export/import; validation bypassed by calling the API directly.","solutions":["Open the saved workflow in the editor and add exactly one workflow_return node wired to the desired output, then re-save and retry the call.","Validate the workflow before calling: assert exactly one node with get_type() == 'workflow_return' exists in the graph.","Remove/hide duplicate workflow_return nodes if you hit the companion 'more than one' error, then re-run.","Catch this ValueError and show a workflow-configuration error to the user pointing at the missing return node."],"exampleFix":"// before: child graph ends at image node\nnodes: [t2i]\n// after\nnodes: [t2i, workflow_return]\nedges: t2i.image -> workflow_return.image","handlingStrategy":"validation","validationCode":"returns = [n for n in wf[\"nodes\"] if n.get(\"data\", {}).get(\"type\") == \"workflow_return\"]\nif len(returns) != 1:\n    raise WorkflowConfigError(\"saved workflow must contain exactly one workflow_return node\")","typeGuard":"def has_single_workflow_return(graph) -> bool:\n    ids = [nid for nid, n in graph.nodes.items() if n.get_type() == \"workflow_return\"]\n    return len(ids) == 1","tryCatchPattern":"try:\n    output = get_child_workflow_return_output(child_session)\nexcept ValueError as e:\n    if \"workflow_return\" in str(e):\n        raise WorkflowConfigError(\"fix saved workflow return node\") from e","preventionTips":["Add exactly one workflow_return node to every workflow intended for call_saved_workflow","Validate saved workflows (single workflow_return) at save/import time","Re-validate workflows authored before the workflow_return requirement existed","Include the return-node check in workflow linters and editor save hooks"],"tags":["invokeai","workflow-call","graph-validation","missing-node"],"backgroundTag":"missing-workflow-return-node","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}