hashicorp/terraform · error

Failed to load the backend state file

Error message

Failed to load the backend state file: %s

What it means

Thrown in Meta.Backend (the main backend/bootstrap entry) when clistate.LocalState.RefreshState fails reading the local backend-cache file at <DataDir>/terraform.tfstate. That file holds the working directory's backend/state_store configuration snapshot, not real Terraform state. A read or parse failure here means the cached backend config is unreadable.

Solutions

  1. Inspect <DataDir>/terraform.tfstate (usually .terraform/terraform.tfstate): confirm it is valid JSON and well-formed.
  2. Restore from version control or a known-good copy if corrupted; otherwise delete it and re-run `tofu init` to regenerate the cache.
  3. Fix filesystem permissions so the current user owns and can read .terraform/.
  4. Avoid running init under a different user (root vs. your account) that leaves the cache unreadable.
  5. If the file is fine but a network/FS transient caused the read failure, retry the command.

Example fix

// before: .terraform/terraform.tfstate is corrupt/hand-edited
tofu plan  # -> Failed to load the backend state file

// after: drop the cache and reinitialize
rm .terraform/terraform.tfstate
tofu init
Defensive patterns

Strategy: validation

Validate before calling

// Validate the backend cache file before Meta.Backend reads it.
func validateBackendCache(dataDir string) error {
    p := filepath.Join(dataDir, DefaultStateFilename) // .terraform/terraform.tfstate
    b, err := os.ReadFile(p)
    if errors.Is(err, os.ErrNotExist) { return nil } // absent is fine
    if err != nil { return err }
    var v json.RawMessage
    if err := json.Unmarshal(b, &v); err != nil {
        return fmt.Errorf("backend cache file %s is corrupt: %w", p, err)
    }
    return nil
}

Try / catch

if err := sMgr.RefreshState(); err != nil {
    if errors.Is(err, os.ErrNotExist) {
        // acceptable: treat as uninitialized
    } else if isJSONParseErr(err) {
        diags = diags.Append(fmt.Errorf("backend cache file is corrupt; run 'tofu init -reconfigure': %s", err))
    } else {
        diags = diags.Append(fmt.Errorf("Failed to load the backend state file: %s", err))
    }
    return nil, diags
}

Prevention

When it happens

Trigger: sMgr.RefreshState() returns an error reading filepath.Join(m.DataDir(), DefaultStateFilename). Triggers: the file exists but is not valid JSON / corrupt state-file version, the file is unreadable due to permissions, or the on-disk bytes were truncated by a crashed write/SIGKILL during a prior init.

Common situations: Disk full or editor crash mid-write corrupted .terraform/terraform.tfstate; a user hand-edited the cache file; permissions changed (root-owned file after running under sudo); NFS/network FS returned an I/O error; mixing OpenTofu and Terraform CLI on the same working dir produced an incompatible cache schema.

Related errors


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

Appendix: source

Thrown at internal/command/meta_backend.go:935

	//
	// The remainder of this code often confusingly refers to this as a "state",
	// so it's unfortunately important to remember that this is not actually
	// what we _usually_ think of as "state", and is instead a local working
	// directory "backend configuration state" that is never persisted anywhere.
	//
	// Since the "real" state has since moved on to be represented by
	// states.State, we can recognize the special meaning of state that applies
	// to this function and its callees by their continued use of the
	// otherwise-obsolete terraform.State.
	// ------------------------------------------------------------------------

	// Get the path to where we store a local cache of backend configuration
	// if we're using a remote backend. This may not yet exist which means
	// we haven't used a non-local backend before. That is okay.
	statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
	sMgr := &clistate.LocalState{Path: statePath}
	if err := sMgr.RefreshState(); err != nil {
		diags = diags.Append(fmt.Errorf("Failed to load the backend state file: %s", err))
		return nil, diags
	}

	// Load the state, it must be non-nil for the tests below but can be empty
	s := sMgr.State()
	if s == nil {
		log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
		s = workdir.NewBackendStateFile()
	} else if s.Backend != nil {
		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
	} else if s.StateStore != nil {
		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q state_store using provider %q, version %s",
			s.StateStore.Type,
			s.StateStore.Provider.Source,
			s.StateStore.Provider.Version)
	} else {
		log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
	}

View on GitHub (pinned to d32a084675)