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
- Remove the duplicate decorator so the class carries only @input_type or @output_type
- If it must be both input- and output-shaped, define a separate output type class or use Input/Output wrappers on plain types
- 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
- Keep exactly one Pulumi type decorator per class
- Search your module for stacked decorators before refactoring between input and output types
- Regenerate provider SDKs instead of hand-adding decorators to generated classes
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
- get can only be used with classes decorated with @input_type
- Expected fn to be callable
- Missing name argument
- Expected name to be a string
- Invalid asset encountered when unmarshalling resource proper
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/32063a5cda082949.
Report an issue: GitHub.