pulumi/pulumi · error · AssertionError

Cannot apply @input_type and @output_type more than once.

Error message

Cannot apply @input_type and @output_type more than once.

What it means

The `@input_type` decorator in sdk/python/lib/pulumi/_types.py:463-468 raises AssertionError when the class is already marked as an input type or an output type. A class may be decorated as a Pulumi type at most once, since the markers drive property translation and dict conversion.

Source

Thrown at sdk/python/lib/pulumi/_types.py:467

def _py_properties(cls: type) -> tuple[tuple[str, str, builtins.property], ...]:
    result: list[tuple[str, str, builtins.property]] = []
    for base in reversed(cls.__mro__):
        for python_name, v in base.__dict__.items():
            if isinstance(v, builtins.property):
                prop = v
                pulumi_name = getattr(prop.fget, _PULUMI_NAME, MISSING)
                if pulumi_name is not MISSING:
                    result.append((python_name, cast(str, pulumi_name), prop))
    return tuple(result)


def input_type(cls: type[T]) -> type[T]:
    """
    Returns the same class as was passed in, but marked as an input type.
    """

    if is_input_type(cls) or is_output_type(cls):
        raise AssertionError(
            "Cannot apply @input_type and @output_type more than once."
        )

    # Get the input properties and mark the class as an input type.
    _process_class(cls, _PULUMI_INPUT_TYPE, is_input=True, setter=True)

    # Helper to create a setter function.
    def create_setter(name: str) -> Callable:
        def setter_fn(self, value):
            set(self, name, value)

        return setter_fn

    # Now, process the class's properties, replacing properties with empty setters with
    # an actual setter.
    for python_name, _, prop in _py_properties(cls):  # type: ignore[arg-type] # https://github.com/python/mypy/issues/11470
        if prop.fset is not None and _utils.is_empty_function(prop.fset):
            setter_fn = create_setter(python_name)

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Remove the duplicate decorator so the class carries only @input_type or @output_type
  2. If it must be both input- and output-shaped, define a separate output type class or use Input/Output wrappers on plain types
  3. Check that generated/vendor code isn't re-decorating an already-decorated class

Example fix

# before
@input_type
@output_type
class Foo:
    ...
# after
@output_type
class Foo:
    ...
Defensive patterns

Strategy: validation

Validate before calling

import pulumi._types as pt
if pt.is_input_type(cls) or pt.is_output_type(cls):
    raise TypeError(f"{cls.__name__} is already decorated as a Pulumi type")
cls = pulumi.input_type(cls)

Type guard

def can_apply_input_type(cls: type) -> bool:
    return not (hasattr(cls, '_pulumi_input_type') or hasattr(cls, '_pulumi_output_type'))

Try / catch

try:
    cls = pulumi.input_type(cls)
except AssertionError as e:
    if "more than once" in str(e):
        pass  # already decorated; use as-is
    else:
        raise

Prevention

When it happens

Trigger: Applying `@input_type` to a class that already has `@input_type` (e.g. stacked decorators, subclass re-decoration, or a module imported twice applying decorators), or applying `@input_type` to a class already decorated with `@output_type`.

Common situations: Refactoring a class from output to input type and leaving both decorators; accidentally stacking decorators during a merge; generated code that re-applies the decorator.

Related errors


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/32063a5cda082949. Report an issue: GitHub.