Loading...
Loading...
### Terraform Version ```shell Terraform v1.15.0 ``` ### Terraform Configuration Files `main.tf` ```terraform terraform { backend "s3" { encrypt = true } } ``` ### Debug Output ``` 2026-04-29T12:06:41.634-0500 [INFO] Terraform version: 1.15.0 2026-04-29T12:06:41.635-0500 [DEBUG] using github.com/hashicorp/go-tfe v1.94.0 2026-04-29T12:06:41.635-0500 [DEBUG] using github.com/hashicorp/hcl/v2 v2.24.0 2026-04-29T12:06:41.635-0500 [DEBUG] using github.com/hashicorp/terraform-svchost v0.2.1 2026-04-29T12:06:41.635-0500 [DEBUG] using github.com/zclconf/go-cty v1.18.0 2026-04-29T12:06:41.635-0500 [INFO] Go runtime version: go1.25.8 2026-04-29T12:06:41.635-0500 [INFO] CLI args: []string{"terraform", "validate"} 2026-04-29T12:06:41.635-0500 [TRACE] Stdout is a terminal of width 231 2026-04-29T12:06:41.635-0500 [TRACE] Stderr is a terminal of width 231 2026-04-29T12:06:41.635-0500 [TRACE] Stdin is a terminal 2026-04-29T12:06:41.635-0500 [DEBUG] Attempting to open CLI config file: /Users/danielcaspi/.terraformrc 2026-04-29T12:06:41.635-0500 [DEBUG] File doesn't exist, but doesn't need to. Ignoring. 2026-04-29T12:06:41.635-0500 [DEBUG] ignoring non-existing provider search directory terraform.d/plugins 2026-04-29T12:06:41.635-0500 [DEBUG] ignoring non-existing provider search directory /Users/danielcaspi/.terraform.d/plugins 2026-04-29T12:06:41.635-0500 [DEBUG] ignoring non-existing provider search directory /Users/danielcaspi/Library/Application Support/io.terraform/plugins 2026-04-29T12:06:41.635-0500 [DEBUG] ignoring non-existing provider search directory /Library/Application Support/io.terraform/plugins 2026-04-29T12:06:41.635-0500 [INFO] CLI command args: []string{"validate"} 2026-04-29T12:06:41.636-0500 [TRACE] terraform.NewContext: starting 2026-04-29T12:06:41.636-0500 [TRACE] terraform.NewContext: complete 2026-04-29T12:06:41.636-0500 [TRACE] building graph for terraform dependencies 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.RootVariableTransformer 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.ModuleTransformer 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.LocalTransformer 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.ReferenceTransformer 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.TransformFilter 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.variableValidationTransformer 2026-04-29T12:06:41.636-0500 [TRACE] variableValidationTransformer: adding validation nodes for any existing variable evaluation nodes 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.RootTransformer 2026-04-29T12:06:41.636-0500 [TRACE] Executing graph transform *terraform.TransitiveReductionTransformer 2026-04-29T12:06:41.636-0500 [TRACE] Completed graph transform: root - terraform.graphNodeRoot ------ 2026-04-29T12:06:41.636-0500 [DEBUG] Starting graph walk: walkInit 2026-04-29T12:06:41.636-0500 [TRACE] vertex "root": starting visit (terraform.graphNodeRoot) 2026-04-29T12:06:41.636-0500 [TRACE] vertex "root": does not belong to any module instance 2026-04-29T12:06:41.636-0500 [TRACE] vertex "root": visit complete 2026-04-29T12:06:41.636-0500 [DEBUG] checking for provisioner in "." 2026-04-29T12:06:41.648-0500 [DEBUG] checking for provisioner in "/usr/local/bin" 2026-04-29T12:06:41.648-0500 [TRACE] terraform.NewContext: starting 2026-04-29T12:06:41.648-0500 [TRACE] terraform.NewContext: complete 2026-04-29T12:06:41.648-0500 [DEBUG] Building and walking validate graph 2026-04-29T12:06:41.648-0500 [TRACE] building graph for walkValidate 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ConfigTransformer 2026-04-29T12:06:41.649-0500 [TRACE] ConfigTransformer: Starting for path: 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ActionTriggerConfigTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ActionInvokePlanTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.RootVariableTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ModuleVariableTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.variableValidationTransformer 2026-04-29T12:06:41.649-0500 [TRACE] variableValidationTransformer: adding validation nodes for any existing variable evaluation nodes 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.LocalTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.OutputTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.checkTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.OrphanResourceInstanceTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.StateTransformer 2026-04-29T12:06:41.649-0500 [TRACE] StateTransformer: pointless no-op call, creating no nodes at all 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.AttachStateTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.OrphanOutputTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.AttachResourceConfigTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.graphTransformerMulti 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Executing graph transform *terraform.externalProviderTransformer 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Completed graph transform *terraform.externalProviderTransformer (no changes) 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Executing graph transform *terraform.ProviderConfigTransformer 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Completed graph transform *terraform.ProviderConfigTransformer (no changes) 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Executing graph transform *terraform.MissingProviderTransformer 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Completed graph transform *terraform.MissingProviderTransformer (no changes) 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Executing graph transform *terraform.ProviderTransformer 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Completed graph transform *terraform.ProviderTransformer (no changes) 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Executing graph transform *terraform.PruneProviderTransformer 2026-04-29T12:06:41.649-0500 [TRACE] (graphTransformerMulti) Completed graph transform *terraform.PruneProviderTransformer (no changes) 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.RemovedModuleTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.AttachSchemaTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ModuleExpansionTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ExternalReferenceTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ReferenceTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.AttachDependenciesTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.attachDataResourceDependsOnTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.DestroyEdgeTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.pruneUnusedNodesTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.TargetsTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.QueryTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ForcedCBDTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.ephemeralResourceCloseTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.CloseProviderTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.CloseRootModuleTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Executing graph transform *terraform.TransitiveReductionTransformer 2026-04-29T12:06:41.649-0500 [TRACE] Completed graph transform: root - *terraform.nodeCloseModule ------ 2026-04-29T12:06:41.649-0500 [DEBUG] Starting graph walk: walkValidate 2026-04-29T12:06:41.649-0500 [TRACE] vertex "root": starting visit (*terraform.nodeCloseModule) 2026-04-29T12:06:41.649-0500 [TRACE] vertex "root": does not belong to any module instance 2026-04-29T12:06:41.649-0500 [TRACE] vertex "root": visit complete ╷ │ Error: Missing required argument │ │ on main.tf line 2, in terraform: │ 2: backend "s3" { │ │ The argument "key" is required, but no definition was found. ╵ ╷ │ Error: Missing required argument │ │ on main.tf line 2, in terraform: │ 2: backend "s3" { │ │ The argument "bucket" is required, but no definition was found. ``` ### Expected Behavior `terraform validate` succeeds. `-backend=false` is an explicit signal that the backend will not be initialized in this working directory; the partial-configuration pattern relies on `bucket`/`key` being supplied during a real `init` via `-backend-config=...`, not at validate time. This pattern has worked since the partial-configuration feature was introduced and is currently documented at https://developer.hashicorp.com/terraform/language/backend. ### Actual Behavior `terraform validate` fails with: ``` │ Error: Missing required argument │ │ on main.tf line N, in terraform: │ N: backend "s3" { │ │ The argument "key" is required, but no definition was found. │ Error: Missing required argument │ │ on main.tf line N, in terraform: │ N: backend "s3" { │ │ The argument "bucket" is required, but no definition was found. ``` ### Steps to Reproduce 1. Save the `main.tf` from above into an empty directory. 2. `terraform init -backend=false` 3. `terraform validate` ### Additional Context This is a CI-relevant regression for any pipeline that does credential-free static validation of Terraform code with a partial backend configuration — a pattern documented at https://developer.hashicorp.com/terraform/language/backend. The behavior change came from #38021, which added backend-block validation to `terraform validate`. That PR was a reasonable response to #30955 (validate previously caught no backend misconfiguration at all). The new validation has clear value when run against a fully-specified backend block. The problem is specifically the interaction with the explicit `-backend=false` flag on a prior `terraform init`. This is in tension with #15895, which framed `terraform validate` as static validation that should work without a backend configured or credentials available, distinguished from `terraform plan`, which performs dynamic validation against the live world. The credential-free init+validate workflow descends directly from that design. **The empty-string variant of partial config is also broken.** It might appear the documented `bucket = ""` workaround would still work, but the s3 backend's value validators now run during validate and reject empty values: ```go // internal/backend/remote-state/s3/backend.go attrPath = cty.GetAttrPath("bucket") if val := obj.GetAttr("bucket"); val.IsNull() { diags = diags.Append(requiredAttributeErrDiag(attrPath)) } else { bucketValidators := validateString{ Validators: []stringValidator{ validateStringNotEmpty, }, } bucketValidators.ValidateAttr(val, attrPath, &diags) } ``` | Backend block | Pre-1.15 validate | Post-1.15 validate | |-----------------------------|-------------------|-----------------------------------| | `{ encrypt = true }` | passes | fails (Missing required argument) | | `{ bucket = "", key = "" }` | passes | fails (validateStringNotEmpty) | Both shapes of the documented partial-config pattern fail. The 1.15 release notes do not flag this as a breaking change, and the linked partial-configuration documentation has not been updated. **Possible fix.** `-backend=false` is an explicit, opt-in signal recorded by init. `terraform validate` could check for it and skip the new backend-block validation when init was run with `-backend=false`, preserving both intents: - #38021's intent: validate catches backend errors in normal use. - #15895's intent: validate is a static, credential-free check that works without a backend configured. A more conservative variant: skip only the "required argument is null" check, while still running per-backend value validators against any explicitly-set values. That would continue to catch typos like `bukket = "..."` while allowing legitimate partial configs to validate. **Workarounds I've found.** None of these are obviously discoverable from the existing documentation: 1. Pin to a pre-1.15 Terraform version in CI. 2. Use a gitignored override file (e.g. `validate_override.tf`) to supply stub `bucket`/`key` during validate-only runs. (Note: backend blocks in override files fully replace, not merge.) 3. Move the backend block to a separate file and remove it before running validate. ### References - #15895 — Proposal: terraform validate for static validation only - #29625 — init with backend=false still attempts to authorize when using S3 backend - #30955 — `terraform validate` allows invalid backend configuration - #38021 — validate: backend block check (the change that introduced this regression) - Docs: https://developer.hashicorp.com/terraform/language/backend (Partial Configuration section) ### Generative AI / LLM assisted development? Claude (Anthropic) assisted with research into prior issues, source-tracing, and drafting this report.
Click on a version to see all relevant bugs
Terraform Integration
Learn more about where this data comes from
Bug Scrub Advisor
Streamline upgrades with automated vendor bug scrubs
BugZero Enterprise
Wish you caught this bug sooner? Get proactive today.