Skip to content

states: fix RootOutputValuesEqual comparing s2 to itself#38181

Merged
SarahFrench merged 3 commits intohashicorp:mainfrom
veeceey:fix/root-output-values-equal-comparison
Feb 18, 2026
Merged

states: fix RootOutputValuesEqual comparing s2 to itself#38181
SarahFrench merged 3 commits intohashicorp:mainfrom
veeceey:fix/root-output-values-equal-comparison

Conversation

@veeceey
Copy link
Copy Markdown
Contributor

@veeceey veeceey commented Feb 14, 2026

Description

RootOutputValuesEqual has a bug where it iterates over s2.RootOutputValues instead of s.RootOutputValues, so it ends up comparing s2 against itself rather than comparing s (the receiver) against s2 (the argument).

// Before (broken): iterates s2 and looks up in s2
for k, v1 := range s2.RootOutputValues {
    v2, ok := s2.RootOutputValues[k]

// After (fixed): iterates s over and looks up in s2
for k, v1 := range s.RootOutputValues {
    v2, ok := s2.RootOutputValues[k]

This means the function always returns true when both states have the same number of output values, even if the values themselves are completely different. This was introduced in #37886.

Impact

This affects refresh-only plan mode (terraform plan -refresh-only), where RootOutputValuesEqual is used to determine whether root output values changed during refresh. With this bug, output value changes during refresh would never cause the plan to be considered applyable, since the comparison always reports equality.

Test plan

  • Added TestStateRootOutputValuesEqual covering identical outputs, differing values, differing sensitivity, differing keys with same count, and differing counts
  • The "different values same key" and "different keys same count" cases specifically catch this bug -- they would have incorrectly returned true before this fix
  • All existing internal/states tests continue to pass

RootOutputValuesEqual had a copy-paste bug where it iterated over
s2.RootOutputValues instead of s.RootOutputValues, effectively
comparing s2 against itself rather than comparing the receiver (s)
against the argument (s2). This meant the function would always
return true as long as both states had the same number of output
values, regardless of whether the actual values differed.

This bug was introduced in hashicorp#37886 and affects refresh-only plan mode,
where RootOutputValuesEqual is used to determine if root output values
changed during refresh, which controls whether the plan is considered
applyable.
@veeceey veeceey requested a review from a team as a code owner February 14, 2026 10:50
@hashicorp-cla-app
Copy link
Copy Markdown

hashicorp-cla-app bot commented Feb 14, 2026

CLA assistant check
All committers have signed the CLA.

@crw
Copy link
Copy Markdown
Contributor

crw commented Feb 16, 2026

Hi @veeceey, is there an issue open for this bug? Thanks.

@crw crw added bug waiting-response An issue/pull request is waiting for a response from the community labels Feb 16, 2026
@veeceey
Copy link
Copy Markdown
Contributor Author

veeceey commented Feb 16, 2026

Hey @crw, I don't think there's an existing issue for this — I found it while reading through the changes in #37886. The bug is pretty straightforward: the loop iterates over s2.RootOutputValues and then also looks up from s2.RootOutputValues, so it's comparing s2 to itself instead of comparing s to s2. Easy to miss in review since the variable names are so similar.

Happy to open an issue first if that's preferred!

Copy link
Copy Markdown
Member

@SarahFrench SarahFrench left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

H! @veeceey , thanks for spotting this!

I don't think you need to open an issue, but it'd be good to establish whether this is a bug that could change user-facing behaviour. If the bug wasn't user facing then we wouldn't include a change log entry.

From the original PR it looks like it could be user facing but only in some specific conditions:

These comparison are not used in any critical codepaths, but do show up in unexpected places, like backend migrations which don't appear to be needed, or incorrect exit codes when there are no changes planned.

I've proposed a change to the change log entry that frames things from a user's perspective. If you're happy to go ahead with that I'm happy to get this merged.

@@ -0,0 +1,5 @@
kind: BUG FIXES
body: 'states: fix `RootOutputValuesEqual` comparing `s2` to itself instead of comparing `s` to `s2`, which caused refresh-only plans to never detect output value changes'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
body: 'states: fix `RootOutputValuesEqual` comparing `s2` to itself instead of comparing `s` to `s2`, which caused refresh-only plans to never detect output value changes'
body: 'states: fixed a bug that caused Terraform to be unable to identify when two states had different output values. This may have caused issues in specific circumstances like backend migrations.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @SarahFrench — that wording is much better! I've pushed the update. Happy to merge whenever you're ready.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@SarahFrench SarahFrench left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again!

@SarahFrench SarahFrench merged commit a5aa6cc into hashicorp:main Feb 18, 2026
7 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

bug waiting-response An issue/pull request is waiting for a response from the community

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants