#24303·pulumi

Targeted preview intermittently reports duplicate URN after removing an untargeted dependency edge

Author: hugo-lorenzo-matoCreated Aug 13, 2026Updated Sep 29, 2026
Labelskind/bugarea/enginearea/targetpulumi/pulumi

What happened?

pulumi preview --target intermittently reports Duplicate resource URN for an existing, untargeted resource after a dependency edge is removed elsewhere in the program. After emitting the diagnostic, the pulumi preview command remains alive until interrupted.

I reduced this from a production targeted-update failure to a disposable local DIY-backend preview. The reproduction uses no cloud resources. Before and after the failed preview, the exported snapshot contains 7 resources, 7 unique URNs, and 0 pending operations.

This reproduces with both Pulumi CLI 3.255.0 and 3.256.0. The clean 3.256.0 stack used Python SDK 3.256.0 and pulumi-random 4.21.1 from its first update, so no CLI, SDK, or provider-version transition is involved.

Minimal program

Pulumi.yaml:

yaml
name: pulumi-target-duplicate-repro
runtime:
  name: python
  options:
    virtualenv: venv

requirements.txt:

pulumi==3.256.0
pulumi-random==4.21.1

__main__.py:

python
import pulumi
import pulumi_random as random

config = pulumi.Config()
capture = config.get_bool("capture") or False

rollback = [
    random.RandomString(f"rollback-{index}", length=12, special=False)
    for index in range(3)
]

target = random.RandomString("target", length=12, special=False)

random.RandomString(
    "snapshot-marker",
    length=12,
    special=False,
    opts=pulumi.ResourceOptions(depends_on=rollback if capture else []),
)

pulumi.export("target", target.result)

Reproduction with an exact 3.256.0 CLI

Download the official 3.256.0 archive for your platform, verify it against the release checksum, extract it, and set PULUMI to that exact binary. For example, after extraction on Linux x86_64:

bash
export PULUMI="$PWD/pulumi-3.256.0/pulumi"
test "$("$PULUMI" version)" = 'v3.256.0' || {
  printf 'expected Pulumi v3.256.0\n' >&2
  exit 1
}
export PULUMI_CONFIG_PASSPHRASE='local-repro-only'

python3 -m venv venv
venv/bin/pip install -r requirements.txt

mkdir backend
"$PULUMI" login "file://$PWD/backend"
"$PULUMI" stack init exact256 --secrets-provider passphrase
"$PULUMI" config set capture true --stack exact256
"$PULUMI" up --stack exact256 --yes --non-interactive

# The state still contains the three old dependencies. This changes only the
# graph returned by the next program evaluation; it is deliberately not applied.
"$PULUMI" config set capture false --stack exact256

"$PULUMI" stack export --stack exact256 | jq -e '
  (.deployment.resources | length) == 7 and
  ([.deployment.resources[].urn] | unique | length) == 7 and
  ((.deployment.pending_operations // []) | length) == 0'

target='urn:pulumi:exact256::pulumi-target-duplicate-repro::random:index/randomString:RandomString::target'
for n in $(seq 1 50); do
  printf 'targeted preview %s\n' "$n"
  timeout --signal=INT --kill-after=5s 20s \
    "$PULUMI" preview --stack exact256 --target "$target" \
      --non-interactive --color never
  rc=$?
  if [ "$rc" -eq 0 ]; then
    continue
  fi
  printf 'targeted preview %s exited %s\n' "$n" "$rc"
  break
done

"$PULUMI" stack export --stack exact256 | jq -e '
  (.deployment.resources | length) == 7 and
  ([.deployment.resources[].urn] | unique | length) == 7 and
  ((.deployment.pending_operations // []) | length) == 0'

Run this only against the disposable local backend: the timeout may send SIGKILL if the command ignores SIGINT, which could leave a backend lock.

The initial update reports six creates (the stack plus five RandomString resources); the checkpoint also contains the provider resource, for seven entries total. Changing capture to false removes the explicit dependency edges only from the next program evaluation; it does not modify the checkpoint because no non-targeted update is applied. The preview targets only the unchanged target resource.

On my clean 3.256.0 stack the first failure appeared on targeted preview 23:

random:index:RandomString rollback-2  error: Duplicate resource URN
'urn:pulumi:exact256::pulumi-target-duplicate-repro::random:index/randomString:RandomString::rollback-2';
try giving it a unique name
Resources:
    5 unchanged
    1 errored

The preview command remained alive until the 20-second timeout sent SIGINT. An A/B stack created and exercised with the 3.255.0 CLI first failed on targeted preview 15. Twenty consecutive non-targeted 3.256.0 previews completed successfully:

bash
for n in $(seq 1 20); do
  printf 'full preview %s\n' "$n"
  timeout --signal=INT --kill-after=5s 20s \
    "$PULUMI" preview --stack exact256 --non-interactive --color never || break
done

Expected behavior

Targeting the unchanged target should not report a duplicate URN for an existing untargeted rollback-* resource. The preview should either complete successfully or fail deterministically with a targeted-update contract error. The pulumi preview command should exit after an engine diagnostic.

Additional context

  • Pulumi 3.256.0 contains #24108, but that change handles Apply over outputs from skipped creates. This reproduction still reports a duplicate URN involving an existing untargeted resource.
  • This may be the same engine defect described in the latest comment on #20852: a targeted operation while the program removes a dependency edge outside the target set, followed by a duplicate on an unrelated bystander. This report adds a minimal cloud-provider-independent program using only pulumi-random and a preview-only variant whose checkpoint remains healthy; the #20852 cases used targeted up and left corrupt snapshots.
  • The original production pulumi up --target failed with the same duplicate-URN diagnostic while writing a DIY-backend snapshot. This issue deliberately uses preview and a disposable local backend to avoid mutating cloud resources.

Environment

  • Ubuntu 24.04 x86_64
  • Python 3.12
  • Pulumi CLI 3.255.0 and 3.256.0
  • Pulumi Python SDK 3.256.0
  • pulumi-random 4.21.1