pulumi/pulumi · error
Cannot read resource whose options are lacking an ID value
Error message
Cannot read resource whose options are lacking an ID value
What it means
Raised by read_resource (the implementation behind CustomResource.get / read_resource_operation): reading an existing resource requires opts.id, because the engine must know which physical resource to fetch. Without an ID there is nothing to read.
Source
Thrown at sdk/python/lib/pulumi/runtime/resource.py:782
core sdk (i.e. Resource.__init__) and then skip that caller type if it is a Resource or ComponentResource.
This is used to compute the source position of the user code that instantiated a resource.
"""
return None if len(stack.frames) < 1 else stack.frames[0].pc
def read_resource(
res: "CustomResource",
ty: str,
name: str,
props: "Inputs",
opts: "ResourceOptions",
typ: Optional[type] = None,
package_ref: Optional[Awaitable[Optional[str]]] = None,
) -> None:
if opts.id is None:
raise Exception("Cannot read resource whose options are lacking an ID value")
log.debug(f"reading resource: ty={ty}, name={name}, id={_safe_str(opts.id)}")
monitor = settings.get_monitor()
# If we have type information, we'll use its and the resource's type/name metadata
# for name translations rather than using the resource's translation methods.
transform_using_type_metadata = typ is not None
# Prepare the resource, similar to a RegisterResource. Reads are deliberately similar to RegisterResource except
# that we are populating the Resource object with properties associated with an already-live resource.
#
# Same as below, we initialize the URN property on the resource, which will always be resolved.
(resolve_urn, res.__dict__["urn"]) = resource_output(res)
# Furthermore, since resources being Read must always be custom resources (enforced in the
# Resource constructor), we'll need to set up the ID field which will be populated at the end of
# the ReadResource call.
#View on GitHub (pinned to 793f7b2e16)
Solutions
- Pass the existing physical ID: MyResource.get("name", "i-abc123") or ResourceOptions(id=...).
- If the id is an Output, use .get inside an apply or id=resource.id reference pattern instead of a resolved-None value.
- If the resource is managed by this stack, construct it normally instead of calling .get().
Example fix
// before
existing = Instance.get("legacy", id=None)
// after
existing = Instance.get("legacy", "i-0abcd1234efgh5678") Defensive patterns
Strategy: validation
Validate before calling
if opts.id is None:
raise ValueError("MyResource.get requires an explicit id") Type guard
def can_read(opts: ResourceOptions) -> bool:
return opts.id is not None Try / catch
try:
existing = Instance.get("name", id)
except Exception as e:
if "lacking an ID value" in str(e):
raise ValueError("provide an explicit physical id to .get()") from e Prevention
- Only call .get() with literal or definitely-known ids.
- Never pass an Optional/Output id directly; resolve it first.
- Use normal resource construction for stack-managed resources instead of .get().
When it happens
Trigger: Calling MyResource.get(name, id=None) or CustomResource.get with ResourceOptions that lack an id, e.g. id coming from an Output/Optional that resolved to None.
Common situations: Using .get() on a resource whose id is derived from another resource's Output that was unknown/empty; copy-pasted get() calls without the id argument; reading a resource that was never created.
Related errors
- Bad alias object {resolved_alias}: {exn}
- While processing resource: {repr(name)}, type: {repr(ty)} Va
- failed to resolve naming config for resource %q: %w
- Cannot read an existing resource unless it has a custom prov
- missing required configuration key: {key}
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/77e409fa2d669f2b.
Report an issue: GitHub.