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

  1. Return a tuple matching RETURN_TYPES: `return (tensor,)`.
  2. Or return a dict keyed by output names, or a NodeOutput, or None for no outputs.
  3. 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

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


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/01e185954abe1719. Report an issue: GitHub.