pulumi/pulumi · error · ValueError
Optional type with no non-None elements
Error message
Optional type with no non-None elements
What it means
Raised by unwrap_optional() in the Pulumi Python component analyzer. Given a typing annotation, it asserts the type is Optional (a Union containing None) and returns the single non-None element. If the annotation is optional-shaped per is_optional() but get_args() yields only NoneType elements (or no args at all), there is nothing to unwrap, so this ValueError is thrown.
Source
Thrown at sdk/python/lib/pulumi/provider/experimental/analyzer.py:881
return _NoneType in get_args(typ)
return False
def is_any(typ: type) -> bool:
return typ is Any
def unwrap_optional(typ: type) -> type:
"""
Returns the first type of the Union that is not NoneType.
"""
if not is_optional(typ):
raise ValueError("Not an optional type")
elements = get_args(typ)
for element in elements:
if element is not _NoneType:
return element
raise ValueError("Optional type with no non-None elements")
def is_not_required(typ: Any) -> bool:
if not _NotRequiredTypes:
return False
return get_origin(typ) in _NotRequiredTypes
def unwrap_not_required(typ: Any) -> type:
if not is_not_required(typ):
raise ValueError(f"{typ} is not a NotRequired type")
return get_args(typ)[0]
def is_required(typ: Any) -> bool:
if not _RequiredTypes:
return False
return get_origin(typ) in _RequiredTypesView on GitHub (pinned to 793f7b2e16)
Solutions
- Fix the annotation to include a concrete inner type, e.g. Optional[str] instead of Optional[None] or bare None
- If the type is built dynamically, verify get_args(typ) before calling unwrap_optional
- Check for typos where the type argument was omitted
- If a property should just be optional, express it as Optional[InnerT] or NotRequired[InnerT] in the args class
Example fix
# before
class MyArgs(TypedDict):
name: Optional[None]
# after
class MyArgs(TypedDict):
name: Optional[str] Defensive patterns
Strategy: type-guard
Validate before calling
from typing import get_args, get_origin, Union, Optional
import types
def check_optional_has_inner(typ):
if get_origin(typ) not in (Union, types.UnionType):
return False
return any(a is not type(None) for a in get_args(typ))
assert check_optional_has_inner(MyArgs.__annotations__["name"]) Type guard
def is_real_optional(typ) -> bool:
import types, typing
origin = get_origin(typ)
if origin is not Union and origin is not types.UnionType:
return False
args = get_args(typ)
return type(None) in args and len(args) > 1 Try / catch
try:
inner = unwrap_optional(typ)
except ValueError:
inner = None # or fix the annotation upstream Prevention
- Always parameterize Optional with a concrete type
- Lint annotations with mypy/pyright to catch Optional[None]
- Prefer NotRequired[InnerT] for TypedDict optionality
When it happens
Trigger: Calling unwrap_optional() (directly or via type analysis in the experimental component provider) with a type whose get_args() contains no element other than NoneType — effectively a degenerate Optional[None]/Union[None] annotation on a component input or property.
Common situations: Annotating a component argument as Optional[None] or Union[None] by mistake; dynamic/programmatically-built annotations where the real inner type was dropped; odd results from get_type_hints on forward refs that resolve to None; typos like Optional() without a parameter.
Related errors
- {typ} is not a NotRequired type
- {typ} is not a Required type
- {typ} is not an output type
- {typ} is not an input type
- {str(inner)}
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/9ee9ecedb724d15d.
Report an issue: GitHub.