hashicorp/terraform · error
state file %q locked, but could not unmarshal lock info: %s
Error message
state file %q locked, but could not unmarshal lock info: %s
What it means
Returned by LocalState.lockInfo (local_state.go:289) when the lock info file exists and was read successfully but json.Unmarshal into statemgr.LockInfo failed. lockInfo is called from both Lock (on contention, to enrich the error) and Unlock (on id mismatch, to enrich the LockError). A corrupt lock info file means Terraform knows a lock exists but cannot tell who holds it.
Source
Thrown at internal/command/clistate/local_state.go:299
if stateName[0] == '.' {
stateName = stateName[1:]
}
return filepath.Join(stateDir, fmt.Sprintf(".%s.lock.info", stateName))
}
// lockInfo returns the data in a lock info file
func (s *LocalState) lockInfo() (*statemgr.LockInfo, error) {
path := s.lockInfoPath()
infoData, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
info := statemgr.LockInfo{}
err = json.Unmarshal(infoData, &info)
if err != nil {
return nil, fmt.Errorf("state file %q locked, but could not unmarshal lock info: %s", s.Path, err)
}
return &info, nil
}
// write a new lock info file
func (s *LocalState) writeLockInfo(info *statemgr.LockInfo) error {
path := s.lockInfoPath()
info.Path = s.Path
info.Created = time.Now().UTC()
err := ioutil.WriteFile(path, info.Marshal(), 0600)
if err != nil {
return fmt.Errorf("could not write lock info for %q: %s", s.Path, err)
}
return nil
}
View on GitHub (pinned to c9def3e214)
Solutions
- Inspect the lock info file: `cat .terraform.tfstate.lock.info` — if it is clearly garbage/truncated, back it up and remove it so future locks write cleanly: `mv .terraform.tfstate.lock.info{,.bak}`.
- If you know no real lock is held, run `terraform force-unlock <id>` with the id from the (partially readable) file, or remove the lock info file and the file lock manually.
- Verify no other Terraform process is actually running before removing the lock file, to avoid clobbering a real lock.
- Keep the lock info file out of sync/edit tools — add `.terraform.tfstate.lock.info` to .gitignore and exclude from cloud-sync folders.
Example fix
# before $ terraform plan state file "terraform.tfstate" locked, but could not unmarshal lock info: invalid character 'r' looking for beginning of object key string # after $ cat .terraform.tfstate.lock.info # confirm it is corrupt $ mv .terraform.tfstate.lock.info .terraform.tfstate.lock.info.bak $ terraform force-unlock <id-from-bak> $ terraform plan
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the lock info file parses before relying on it
info, err := s.lockInfo()
if err != nil {
// likely corrupt; surface to caller and do not assume ownership
return nil, fmt.Errorf("lock info unreadable; manual intervention required: %w", err)
}
return info, nil Type guard
// LockInfoUsable returns true if the on-disk lock info file parses cleanly.
func (s *LocalState) LockInfoUsable() bool {
_, err := s.lockInfo()
return err == nil
} Try / catch
// On a parse error, treat the lock as unidentifiable and require explicit operator action rather than auto-unlocking.
if _, err := s.lockInfo(); err != nil {
return fmt.Errorf("lock info file is corrupt (%w); inspect %s and run terraform force-unlock manually", err, s.lockInfoPath())
} Prevention
- Never hand-edit .terraform.tfstate.lock.info.
- Add the lock info file to .gitignore and exclude from cloud sync.
- Keep Terraform upgraded within the same major line to preserve the LockInfo schema.
- Transfer state dirs with rsync/tar (binary-safe), not ASCII-mode FTP.
When it happens
Trigger: The file at lockInfoPath() (e.g. .terraform.tfstate.lock.info) is truncated, hand-edited, or partially written due to a crash during writeLockInfo; a prior Terraform version wrote a different JSON schema; an external tool or editor modified the file; disk corruption; a merge conflict marker ended up in the file.
Common situations: A Terraform crash mid-write left a half-written lock info file; a user manually edited .terraform.tfstate.lock.info; an upgrade changed the LockInfo schema; a sync tool (Dropbox/OneDrive) corrupted the file mid-sync; the file was transferred via FTP in ASCII mode and line endings broke JSON.
Related errors
- blob metadata %q was empty
- state %q already locked
- LocalState not locked
- invalid lock id: %q. current id: %q
- could not write lock info for %q: %s
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/67733bed246c2f1f.
Report an issue: GitHub.