hashicorp/terraform · error
failed to decode backend config
Error message
failed to decode backend config: %w
What it means
Returned by BackendConfigState.PlanData when s.Config(schema) fails to decode the stored backend config against the provided backend schema while building a plan's Backend block. The %w wraps the underlying decoder error (typically a cty/schema conformance failure on the raw config bytes).
Solutions
- Run `terraform init` against the current binary so the backend config is re-validated and re-written.
- Inspect the wrapped error for the offending argument; fix it in the `backend "<type>"` block and re-init.
- If the binary was downgraded, upgrade back to (or past) the version that wrote the state file.
- As a last resort, remove the stale .terraform/terraform.tfstate backend record and re-init from scratch.
Example fix
# before — error on plan after switching terraform versions terraform plan # after — re-initialise so the backend config matches the binary terraform init terraform plan
Defensive patterns
Strategy: validation
Validate before calling
// pre-flight before calling PlanData: confirm config decodes against current schema
if _, err := s.Config(schema); err != nil {
return nil, fmt.Errorf("re-run `terraform init`: backend config no longer matches schema: %w", err)
} Try / catch
configVal, err := s.Config(schema)
if err != nil {
return nil, fmt.Errorf("failed to decode backend config: %w; run `terraform init` to reconcile", err)
} Prevention
- Always run `terraform init` after upgrading or downgrading Terraform.
- Keep the backend block under version control and review changes.
- Don't hand-edit .terraform/terraform.tfstate.
When it happens
Trigger: Calling BackendConfigState.PlanData during plan creation when the on-disk backend config (in .terraform/terraform.tfstate) no longer matches the compiled-in backend schema — unknown arguments, wrong types, or a backend type whose schema changed between Terraform versions.
Common situations: Downgrading Terraform/OpenTofu after a newer backend wrote config using arguments the older build doesn't know, hand-editing the backend state file, or a backend plugin schema change without `terraform init` re-running.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode state_store config
- error determining current workspace when initializing a…
- error when preparing state store config for planfile
- saved backend configuration is invalid
- acl value invalid, expected
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/24394346dc5d123d.
Report an issue: GitHub.
Appendix: source
Thrown at internal/command/workdir/backend_config_state.go:86
return nil
}
// PlanData produces an alternative representation of the receiver that is
// suitable for storing in a plan. The current workspace must additionally
// be provided, to be stored alongside the backend configuration.
//
// The backend configuration schema is required in order to properly
// encode the backend-specific configuration settings.
//
// As backends are not implemented by providers, the provider schema argument should always be nil
func (s *BackendConfigState) PlanData(schema *configschema.Block, _ *configschema.Block, workspaceName string) (*plans.Backend, error) {
if s == nil {
return nil, nil
}
configVal, err := s.Config(schema)
if err != nil {
return nil, fmt.Errorf("failed to decode backend config: %w", err)
}
return plans.NewBackend(s.Type, configVal, schema, workspaceName)
}
func (s *BackendConfigState) DeepCopy() *BackendConfigState {
if s == nil {
return nil
}
ret := &BackendConfigState{
Type: s.Type,
Hash: s.Hash,
}
if s.ConfigRaw != nil {
ret.ConfigRaw = make([]byte, len(s.ConfigRaw))
copy(ret.ConfigRaw, s.ConfigRaw)
}
return retView on GitHub (pinned to d32a084675)