Loading...
Loading...
### Terraform Version ```shell Terraform v1.16.0 on linux_amd64 Regression. Works correctly on 1.15.9 ``` ### Terraform Configuration Files ```bash #!/usr/bin/env bash # Usage: ./repro.sh [path/to/terraform] (defaults to `terraform` on PATH) # The failure is nondeterministic. Run this a few times. set -e TF="${1:-terraform}" cd "$(mktemp -d)" cat > main.tf <<'EOF' resource "terraform_data" "r2" {} resource "terraform_data" "r3" { triggers_replace = ["v1"] } EOF "$TF" init -input=false > /dev/null "$TF" apply -auto-approve -input=false > /dev/null cat > main.tf <<'EOF' resource "terraform_data" "r1" { lifecycle { create_before_destroy = true } } resource "terraform_data" "r2" { depends_on = [terraform_data.r1] lifecycle { replace_triggered_by = [terraform_data.r3] } } resource "terraform_data" "r3" { triggers_replace = ["v2"] } EOF "$TF" apply -auto-approve -input=false ``` ### Debug Output https://gist.github.com/Nilsils/525625cdbde8756db41710d426dfbe02 ### Expected Behavior Step 2 applies, creating `r1` and replacing `r2` and `r3`: ``` Apply complete! Resources: 3 added, 0 changed, 2 destroyed. ``` That is what 1.15.9 does on 100 attempts out of 100, deterministically. The configuration's graph is acyclic. `terraform graph` emits exactly two edges: ``` "terraform_data.r2" -> "terraform_data.r1"; "terraform_data.r2" -> "terraform_data.r3"; ``` ### Actual Behavior ``` Error: Cycle: terraform_data.r2 terraform_data.r2 (expand) terraform_data.r3 terraform_data.r3 (destroy) terraform_data.r2 (destroy) ``` **Please run it more than once.** The same input gives three different results on 1.16.0, and none of them is correct. Over 100 runs, each started from an identical step 1 state: | outcome | count | |---|---| | `Error: Cycle` | 88 | | `Error: Provider produced inconsistent final plan` | 4 | | `Apply complete` but **0 destroyed** | 8 | | `Apply complete`, 2 destroyed (correct) | **0** | The node set printed in the cycle also varies between failing runs. A single run that happens not to cycle is not a passing run: it either fails differently or silently skips both destroys. `terraform plan` fails the same way, roughly 3 attempts in 4. When a plan is produced it is correct. ### Steps to Reproduce 1. Save the script above as `repro.sh`. 2. Run it, a few times: ``` $ bash repro.sh # uses `terraform` from PATH $ bash repro.sh /path/to/terraform # or name a binary ``` ### Additional Context **Workaround:** declare `create_before_destroy = true` on `r2`. That gives the correct result on 60 attempts out of 60. It has to be `r2`: putting it on `r3` also stops the cycle, but then silently loses one of the two destroys. State is not corrupted by a failed apply; it is byte-identical afterwards. Broken from `1.16.0-alpha20260715` onward, including 1.16.0, 1.17.0-alpha20260827 and current `main` (`7f2c2291b4`). Bisected by building from source: `038c6f72` ("Missing check for orphaned CBD node", PR #38840) is the first bad commit, and its parent is clean. ### References - #39076 regresses at this same commit and is a different defect. ### Generative AI / LLM assisted development? The configuration was not written by an LLM. It was produced by an automated metamorphic testing pipeline that generates equivalent Terraform programs, then reduced by hand to the three resources above. Claude was used to assist with writing this report, with the reduction, the bisect and the version sweep. Every number in it comes from running the configurations against the named binaries.
Click on a version to see all relevant bugs
Terraform Integration
Learn more about where this data comes from
BugZero Plan
Streamline upgrades with automated vendor bug scrubs
BugZero Prevent
Wish you caught this bug sooner? Get proactive today.