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

  1. Pass the existing physical ID: MyResource.get("name", "i-abc123") or ResourceOptions(id=...).
  2. If the id is an Output, use .get inside an apply or id=resource.id reference pattern instead of a resolved-None value.
  3. 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

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


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