hashicorp/terraform · error

Unhandled backend configuration state. This is a bug…

Error message

Unhandled backend configuration state. This is a bug. Please
report this error with the following information.

Backend Config Nil: %v
Saved Backend Empty: %v
StateStore Config Nil: %v
Saved StateStore Empty: %v

What it means

A defensive default branch in the big switch over (backendConfig nil?, saved backend empty?, stateStoreConfig nil?, saved stateStore empty?) combinations. Every legitimate combination has an explicit case; reaching default means a combination the authors believed impossible occurred, so it is reported as a Terraform bug with the four boolean flags for triage.

Solutions

  1. Capture the four booleans from the message and report them to the OpenTofu issue tracker as instructed.
  2. Recover locally: back up then delete .terraform/terraform.tfstate and run `tofu init -reconfigure` to rebuild a clean cache.
  3. Audit the configuration to ensure exactly one of backend/state_store is configured, not both.
  4. If reproducing after a version change, note the versions in the bug report and check the migration matrix against your cache shape.

Example fix

// before: cache file has both Backend and StateStore populated
tofu init  # -> Unhandled backend configuration state. This is a bug.

// after: reset the working-directory cache and reinitialize cleanly
cp .terraform/terraform.tfstate /tmp/tfstate.bak
rm .terraform/terraform.tfstate
tofu init -reconfigure
Defensive patterns

Strategy: validation

Validate before calling

// Reject configs that could produce an unhandled combination before init.
func validateBackendVsStateStore(cfg *configs.Module) error {
    if cfg.Backend != nil && cfg.StateStore != nil {
        return fmt.Errorf("configuration declares both backend %q and state_store %q; choose one", cfg.Backend.Type, cfg.StateStore.Type)
    }
    return nil
}

// Also validate the cache file does not have both Backend and StateStore set.
func validateCacheNotBoth(cachePath string) error {
    s, err := workdir.LoadBackendStateFile(cachePath)
    if err != nil || s == nil { return err }
    if s.Backend != nil && !s.Backend.Empty() && s.StateStore != nil && !s.StateStore.Empty() {
        return fmt.Errorf("cache file has both backend and state_store; run 'tofu init -reconfigure'")
    }
    return nil
}

Try / catch

// This is a bug branch; do not 'handle' silently. Surface the four booleans and
// advise reconfigure.
diags = diags.Append(fmt.Errorf(
    "Unhandled backend configuration state (backendCfgNil=%v savedBackendEmpty=%v stateStoreCfgNil=%v savedStateStoreEmpty=%v); run 'tofu init -reconfigure'",
    backendConfig == nil, s.Backend.Empty(), stateStoreConfig == nil, s.StateStore.Empty(),
))

Prevention

When it happens

Trigger: Hit only when the four-way combination is one the switch does not list, e.g. both backendConfig and stateStoreConfig non-nil simultaneously, or both saved Backend and saved StateStore non-empty simultaneously (a corrupt cache file should not have both). Real triggers are cache-file corruption or a future code path that sets the config fields inconsistently.

Common situations: Working directory's .terraform/terraform.tfstate has both Backend and StateStore populated (manual edit or bug in a prior migration); a config has both a `backend` and a `state_store` block somehow parsed (which earlier validation should reject); downgrade/upgrade across a version that wrote a different cache shape.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/05b4e745fef0de7c. Report an issue: GitHub.

Appendix: source

Thrown at internal/command/meta_backend.go:1305

			return savedStateStore, diags
		}

		initReason, ssDiags := m.determineStateStoreInitReason(s.StateStore, stateStoreConfig, opts.Locks)
		diags = diags.Append(ssDiags)
		if ssDiags.HasErrors() {
			return nil, diags
		}

		// Regardless of whether this code is invoked in an init or non-init command,
		// we advise users to choose between:
		// 1. terraform state migrate
		// 2. terraform init -reconfigure
		diags = diags.Append(errStateStoreInitDiag(initReason))
		return nil, diags

	default:
		diags = diags.Append(fmt.Errorf(
			"Unhandled backend configuration state. This is a bug. Please\n"+
				"report this error with the following information.\n\n"+
				"Backend Config Nil: %v\n"+
				"Saved Backend Empty: %v\n"+
				"StateStore Config Nil: %v\n"+
				"Saved StateStore Empty: %v\n",
			backendConfig == nil,
			s.Backend.Empty(),
			stateStoreConfig == nil,
			s.StateStore.Empty(),
		))
		return nil, diags
	}
}

// determineInitReason is used in non-Init commands to interrupt the command early and prompt users to instead run an init command.
// That prompt needs to include the reason why init needs to be run, and it is determined here.
//

View on GitHub (pinned to d32a084675)