pulumi/pulumi · error · TypeError
Can't set key values on non-dict variables.
Error message
Can't set key values on non-dict variables.
What it means
Pulumi's `_utils.py` provides a descriptor-based lazy property wrapper whose `__setitem__` delegates to the wrapped getter, mutates the returned mapping, and writes it back via the setter. If the getter returns anything that is not a `dict`, key assignment is impossible and this TypeError is raised. It protects against silently overwriting non-dict values.
Source
Thrown at sdk/python/lib/pulumi/_utils.py:228
def fdel(self, *_):
return self._data.reset()
def __set__(self, _, v: typing.Any):
return self.fset(v)
def __get__(self, obj: typing.Optional[typing.Any], _):
if obj is None or isinstance(self.fget(), dict):
return self
return self.fget()
def __delete__(self, *_):
self.fdel()
def __setitem__(self, key, value):
data = self.fget()
if not isinstance(data, dict):
raise TypeError("Can't set key values on non-dict variables.")
data[key] = value
return self.fset(data)
def __dict__(self):
if not isinstance(self.fget(), dict):
raise TypeError(f"{self} is not a dictionary")
return self.fget()
def __getitem__(self, key):
return self.fget()[key]
def get(self, key, default=None):
data = self.fget()
if not isinstance(data, dict):
raise TypeError("Can't get key values on non-dict variables.")
return data.get(key, default)
def __contains__(self, element):View on GitHub (pinned to 793f7b2e16)
Solutions
- Make the underlying getter return a dict (initialize it to `{}` instead of None).
- Assign the whole value instead of item assignment: `obj.prop = {'key': value}`.
- If the value is genuinely a list, build the list separately and assign it via the setter.
- Guard with isinstance before item assignment.
Example fix
// before
obj.tags["env"] = "prod" # getter returns None
// after
if isinstance(obj.tags, dict):
obj.tags["env"] = "prod"
else:
obj.tags = {"env": "prod"} Defensive patterns
Strategy: type-guard
Validate before calling
value = obj.prop
if not isinstance(value, dict):
obj.prop = {}
obj.prop[key] = new_value Type guard
def is_dict_value(v) -> TypeGuard[dict]:
return isinstance(v, dict) Try / catch
try:
obj.prop[key] = value
except TypeError as e:
if "non-dict" in str(e):
obj.prop = {key: value}
else:
raise Prevention
- Initialize wrapped properties to {} not None.
- Keep getters returning dict only.
- Assign whole dicts instead of item assignment when type is uncertain.
When it happens
Trigger: Executing `obj.prop[key] = value` where `obj.prop` is the wrapped property and its getter returns a list, string, None, or custom object instead of a dict.
Common situations: A config/opts getter that was changed to return a list or Optional[dict]; the underlying attribute still None before initialization; a refactored getter returning a dataclass or named tuple.
Related errors
- {self} is not a dictionary
- Can't get key values on non-dict variables.
- virtualenv option must be a string
- typechecker option must be a string
- toolchain option must be a string
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/5e772720f877641b.
Report an issue: GitHub.