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
- Inspect <DataDir>/terraform.tfstate (usually .terraform/terraform.tfstate): confirm it is valid JSON and well-formed.
- Restore from version control or a known-good copy if corrupted; otherwise delete it and re-run `tofu init` to regenerate the cache.
- Fix filesystem permissions so the current user owns and can read .terraform/.
- Avoid running init under a different user (root vs. your account) that leaves the cache unreadable.
- 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
- Never hand-edit .terraform/terraform.tfstate.
- Commit/restore this file via CI cache keyed on config hash, or let init regenerate it.
- Run init under the same user that will run plan/apply to keep file ownership consistent.
- Avoid running init under sudo/root which can leave the cache unreadable.
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
- Failed to load state
- Error creating temporary directory
- Failed to set new workspace
- encountered a malformed backend state file that contains…
- encountered a malformed backend state file with a…
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)