Skip to content

Fix dropping optional fields with schema defaults - #3484

Open
jdavredbeard wants to merge 2 commits into
mainfrom
jdavenport/fix-default-value-drop
Open

Fix dropping optional fields with schema defaults#3484
jdavredbeard wants to merge 2 commits into
mainfrom
jdavenport/fix-default-value-drop

Conversation

@jdavredbeard

Copy link
Copy Markdown

Summary

  • Fixes extractSchemaInputsObject dropping optional fields whose value matches the schema default (e.g. force_destroy: false with Default: false). Previously these were treated as "zero values" and dropped, causing spurious diffs on next preview when PlanResourceChange re-applied the same default.
  • When Read returns null for a field that has a schema default, injects the default value to prevent a null → default diff after pulumi import.

Fixes #2436

Test plan

  • Added TestExtractSchemaInputsDefaultInjection covering null injection, default retention, and no-default drop cases
  • Added TestIsDefaultOrZeroValueBehavior validating understanding of the helper function
  • Updated existing TestExtractDefaultSecretInputs, TestExtractDefaultIntegerInputs, and TestExtractInputsFromOutputsSdkv2 expectations
  • CI passes

🤖 Generated with Claude Code

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.21%. Comparing base (b4f55d2) to head (651abfb).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3484      +/-   ##
==========================================
- Coverage   70.21%   70.21%   -0.01%     
==========================================
  Files         350      350              
  Lines       38524    38530       +6     
==========================================
+ Hits        27049    27053       +4     
- Misses       9615     9617       +2     
  Partials     1860     1860              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jdavredbeard
jdavredbeard marked this pull request as ready for review June 22, 2026 15:46

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One Important finding: the null injection block in schema.go does not guard against getDefaultValue returning a non-nil error as interface{}, which would corrupt state for providers with failing DefaultFunc implementations. See the inline comment for the fix. One Nit on test comment framing.

Reviewed by Internal Trusted PR Reviewer

To install this agentic workflow, run

gh aw add pulumi-labs/gh-aw-internal/.github/workflows/gh-aw-pr-review.md@242988150273951aad5f67b008256266bdff6112

Comment thread pkg/tfbridge/schema.go
// This handles the case where Read didn't populate the field but
// PlanResourceChange will apply the default, causing a null → default diff.
if ev.IsNull() && allowDrop && !etfs.Required() {
if dv := getDefaultValue(etfs, eps); dv != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important — bug

getDefaultValue can return a non-nil error as interface{} when DefaultFunc fails with an error other than ErrSchemaDefaultValue (see schema.go:~1855, which does return err). The dv != nil guard passes for errors, so resource.NewPropertyValue(dv) receives an error object and writes a corrupted value into the property map.

Before this PR, a null field fell through to v[k] = ev (storing null safely). This block regresses that for providers whose DefaultFunc returns a non-ErrSchemaDefaultValue error.

Add an explicit error guard:

if ev.IsNull() && allowDrop && !etfs.Required() {
    if dv := getDefaultValue(etfs, eps); dv != nil {
        if _, isErr := dv.(error); !isErr {
            v[k] = resource.NewPropertyValue(dv)
            continue
        }
    }
}
Why this matters

getDefaultValue explicitly returns the raw error as interface{} for non-ErrSchemaDefaultValue failures. The existing isDefaultOrZeroValue caller is safe because comparing an error-typed dv to v.V simply returns false. The null injection path lacks this protection, introducing a state corruption regression.

Comment thread pkg/tfbridge/schema_test.go Outdated
Comment on lines +2720 to +2723
// With the fix for #2436, inputD is retained because its value
// matches the schema default. Previously it was dropped, causing
// a spurious diff on next preview.
"inputD": "input_d_default",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit — CLAUDE.md adherence

These comments reference the PR fix by issue number and describe prior behavior ("With the fix for #2436...", "Previously it was dropped"). The same pattern appears at lines 2795 and 3658. CLAUDE.md says to not reference the current task, fix, or callers — that context belongs in the PR description and rots as the codebase evolves.

Rephrase to describe the invariant instead:

Suggested change
// With the fix for #2436, inputD is retained because its value
// matches the schema default. Previously it was dropped, causing
// a spurious diff on next preview.
"inputD": "input_d_default",
// inputD is retained: its value matches the schema default, and dropping it would
// cause a spurious diff when PlanResourceChange re-applies the default on next preview.
"inputD": "input_d_default",
Why this matters

CLAUDE.md: "Don't reference the current task, fix, or callers ('used by X', 'added for the Y flow', 'handles the case from issue #123'), since those belong in the PR description and rot as the codebase evolves."

jdavredbeard and others added 2 commits June 23, 2026 11:18
When extractSchemaInputsObject encounters an optional field whose value
matches its schema default (e.g. `false` for `Default: false`), it was
being dropped as a "zero value". This caused spurious diffs on the next
preview when PlanResourceChange re-applied the same default.

Additionally, when Read returns null for a field that has a schema
default, inject the default value to prevent a null → default diff
after import.

Fixes #2436

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tValue

- Add error type check to prevent getDefaultValue returning a raw error
  from corrupting state via resource.NewPropertyValue
- Rephrase test comments to describe invariants instead of referencing
  issue numbers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jdavredbeard
jdavredbeard force-pushed the jdavenport/fix-default-value-drop branch from 11051ae to 651abfb Compare June 23, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avoid default value diffs upon pulumi import

1 participant