Comfy-Org/ComfyUI · error · Exception
Invalid return type from node: {type(to_return)}
Error message
Invalid return type from node: {type(to_return)} What it means
ComfyUI normalizes every node's execute() return value into a NodeOutput, accepting None, NodeOutput, plain tuple, dict, or ExecutionBlocker. Anything else (an int, list, string, generator, ...) means the node author violated the return contract and the executor raises immediately instead of producing corrupt output.
Source
Thrown at comfy_api/latest/_io.py:2002
return "EXECUTE_NORMALIZED_ASYNC"
return "EXECUTE_NORMALIZED"
@final
@classmethod
def EXECUTE_NORMALIZED(cls, *args, **kwargs) -> NodeOutput:
to_return = cls.execute(*args, **kwargs)
if to_return is None:
to_return = NodeOutput()
elif isinstance(to_return, NodeOutput):
pass
elif isinstance(to_return, tuple):
to_return = NodeOutput(*to_return)
elif isinstance(to_return, dict):
to_return = NodeOutput.from_dict(to_return)
elif isinstance(to_return, ExecutionBlocker):
to_return = NodeOutput(block_execution=to_return.message)
else:
raise Exception(f"Invalid return type from node: {type(to_return)}")
if to_return.expand is not None and not cls.SCHEMA.enable_expand:
raise Exception(f"Node {cls.__name__} is not expandable, but expand included in NodeOutput; developer should set enable_expand=True on node's Schema to allow this.")
return to_return
@final
@classmethod
async def EXECUTE_NORMALIZED_ASYNC(cls, *args, **kwargs) -> NodeOutput:
to_return = await cls.execute(*args, **kwargs)
if to_return is None:
to_return = NodeOutput()
elif isinstance(to_return, NodeOutput):
pass
elif isinstance(to_return, tuple):
to_return = NodeOutput(*to_return)
elif isinstance(to_return, dict):
to_return = NodeOutput.from_dict(to_return)
elif isinstance(to_return, ExecutionBlocker):
to_return = NodeOutput(block_execution=to_return.message)View on GitHub (pinned to 1c6d8d45b3)
Solutions
- Return a tuple matching RETURN_TYPES: `return (tensor,)`.
- Or return a dict keyed by output names, or a NodeOutput, or None for no outputs.
- Never return a raw list — convert with tuple(my_list).
Example fix
# before
def execute(self, ...):
return [img1, img2] # list -> Exception
# after
def execute(self, ...):
return (stack([img1, img2]),) # tuple matching RETURN_TYPES Defensive patterns
Strategy: type-guard
Validate before calling
result = cls.execute(*args, **kwargs)
assert result is None or isinstance(result, (NodeOutput, tuple, dict, ExecutionBlocker)), (
f"bad return type {type(result)}"
) Type guard
def valid_node_return(value) -> bool:
return value is None or isinstance(value, (NodeOutput, tuple, dict, ExecutionBlocker)) Prevention
- Always return tuples matching RETURN_TYPES.
- Convert lists with tuple(list) before returning.
- Write a smoke test that calls execute and asserts the return type.
When it happens
Trigger: A custom node's execute returns e.g. a list comprehension of tensors, a bare string, or a numpy array — types accepted by legacy ComfyUI OUTPUT_NODE conventions but not by EXECUTE_NORMALIZED.
Common situations: Porting legacy nodes that returned lists; typos like 'return results' instead of 'return (results,)'; generators from comprehension refactors.
Related errors
- Node {cls.__name__} is not expandable, but expand included i
- INVALID_TAG_FILTER
- INVALID_QUERY
- Bad block_type
- Attempt to create ChromaRadiance object without setting oper
AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14).
Data as JSON: /api/errors/01e185954abe1719.
Report an issue: GitHub.