December 18, 2024·4 min read·engineeringterraformplanscorrectnesserrors

Terraform 1.10.3 preserves a plan container when encoding fails

Terraform 1.10.3 repairs a panic on the plan-encoding error path. The patch preserves a valid result container alongside the error, without treating incomplete encoding as success.

A planning failure still needs a representation that the rest of Terraform can process. Terraform 1.10.3 fixes an error path where plan encoding returned no changes container, leading to a panic after the original failure. The patch keeps a valid container available while retaining the encoding error.

The 1.10.3 release, dated 18 December, contains this single listed fix. The implementation in pull request 36212 explains the contract directly. Terraform builds a plan even when errors have occurred, so the encoder must return a valid changes object along with the failure.

I consider error handling part of the infrastructure tool's operating behavior. A rejected plan should provide the information needed to understand the rejection. A secondary panic can replace that explanation with a failure in code that was supposed to preserve it.

The result and error carry different information

The affected method is Changes.Encode. It constructs a ChangesSrc container, then encodes resource and output changes into that container. Resource encoding first needs the relevant provider schema, because the schema supplies the implied type needed to encode the change.

Several operations can fail. The provider may be absent from the supplied schemas. The resource may lack a usable schema block. Encoding an individual resource change can return an error, and encoding an output change can fail too. Before the correction, these paths returned nil alongside the error.

The patch changes each of those returns to supply the container already allocated at the start of the method. The error remains intact, including its description of the missing provider, missing schema or encoding failure. This does not cause an invalid plan to become a successful result.

Instead, it separates structural validity from successful completion. The caller receives the error that establishes failure and a container that satisfies the surrounding plan-building process's assumptions. An allocated container can hold successfully encoded entries encountered before the failure, but those entries do not imply that encoding the complete plan succeeded.

That distinction is easy to lose in a language interface returning both a pointer and an error. Some functions use nil as the expected result whenever an error exists. This encoder has a different contract because later plan construction still needs the result structure during failure handling. The correction makes the implementation conform to that requirement.

The test deliberately retains the failure

The new regression test supplies an output change with a marked value that cannot be encoded through that path. It expects the encoder to return an error. It separately asserts that the returned changes container is non-nil.

This is a useful test because it rejects both obvious mistakes. Suppressing the error would fail the first assertion. Returning the error with no container would fail the second. The desired behavior is a failed encoding operation with enough structure for its caller to handle that failure without another crash.

The test does not require a cloud account or a provider schema. Its change list contains no resource changes, so the output encoding failure isolates the return-value contract. That makes the regression about the error path itself, without depending on a remote API producing a particular failure.

The release notes do not claim that every encoding error has the same underlying cause. Nor does this patch establish that a panic of this kind means a provider changed infrastructure incorrectly. Its scope is the handling of an encoding failure after the plan changes already exist.

December also clarified saved-plan inputs

The 1.10.3 tagged changelog includes the earlier December maintenance releases. Those changes provide context for the plan/apply boundary being stabilized. Version 1.10.1 corrected parsing of complex input values supplied through environment variables during apply.

The parsing patch in pull request 36121 switches the apply path to the variable parsing logic that uses module variable declarations. It then compares the parsed values with the values saved in the plan. A textual environment value and a typed configuration value need the same interpretation before that comparison is meaningful.

Version 1.10.2 addressed a separate saved-plan interaction. An automatically loaded variable file could contain a value that had been overridden while planning. Applying the saved plan would encounter the automatic file again and incorrectly diagnose a changed variable.

The fix in pull request 36180 distinguishes values loaded automatically from explicit attempts to override a saved plan's ordinary inputs. Its regression creates a plan with an explicitly supplied variable file that overrides terraform.tfvars, then applies that plan without resupplying the override file. The saved plan remains the source of the ordinary planned input.

These earlier patches do not explain the specific encoding panic, and they should not be conflated with it. They address how values reach and cross the saved-plan boundary. Version 1.10.3 addresses how the tool represents failure while encoding the resulting changes.

I find the new regression's two assertions the clearest statement of this release's contribution. Encoding must still report its error, and the result must still provide a valid container. The caller can then process the failed plan without losing the original problem to a nil-result panic.