{"record":{"id":"67733bed246c2f1f","repo":"hashicorp/terraform","slug":"state-file-q-locked-but-could-not-unmarshal-lock","errorCode":null,"errorMessage":"state file %q locked, but could not unmarshal lock info: %s","messagePattern":"state file %q locked, but could not unmarshal lock info: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/command/clistate/local_state.go","lineNumber":299,"sourceCode":"\tif stateName[0] == '.' {\n\t\tstateName = stateName[1:]\n\t}\n\n\treturn filepath.Join(stateDir, fmt.Sprintf(\".%s.lock.info\", stateName))\n}\n\n// lockInfo returns the data in a lock info file\nfunc (s *LocalState) lockInfo() (*statemgr.LockInfo, error) {\n\tpath := s.lockInfoPath()\n\tinfoData, err := ioutil.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinfo := statemgr.LockInfo{}\n\terr = json.Unmarshal(infoData, &info)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"state file %q locked, but could not unmarshal lock info: %s\", s.Path, err)\n\t}\n\treturn &info, nil\n}\n\n// write a new lock info file\nfunc (s *LocalState) writeLockInfo(info *statemgr.LockInfo) error {\n\tpath := s.lockInfoPath()\n\tinfo.Path = s.Path\n\tinfo.Created = time.Now().UTC()\n\n\terr := ioutil.WriteFile(path, info.Marshal(), 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not write lock info for %q: %s\", s.Path, err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":281,"sourceCodeEnd":316,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/command/clistate/local_state.go#L281-L316","documentation":"Thrown by LocalState.lockInfo when the on-disk .tflock file was read successfully but its contents could not be unmarshalled as JSON into statemgr.LockInfo. The lock metadata file is written by writeLockInfo as info.Marshal(); a parse failure means the file is corrupt or was replaced by something else.","triggerScenarios":"The lock info file exists and is readable but contains non-JSON or wrong-shape JSON; another tool overwrote it; a partial write left truncated JSON; a user manually created the .tflock with invalid content.","commonSituations":"An external process or script wrote the .tflock file incorrectly; a disk error truncated it; a previous crash during writeLockInfo (the code uses WriteFile, not atomic) left a partial file; manual fiddling with lock files.","solutions":["Inspect the lock info file (path returned by lockInfoPath, typically <statefile>.tflock) and validate its JSON.","If no operation is actually running, remove the stale/corrupt lock file and run `terraform force-unlock <id>` if a lock ID is still tracked.","Stop writing lock files by hand or with external scripts — let Terraform manage them.","Investigate why the file was truncated (disk health, concurrent writers)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func lockInfoReadable(path string) (*statemgr.LockInfo, error) {\n    data, err := os.ReadFile(path)\n    if err != nil { return nil, err }\n    var info statemgr.LockInfo\n    if err := json.Unmarshal(data, &info); err != nil {\n        return nil, fmt.Errorf(\"corrupt lock info at %s\", path)\n    }\n    return &info, nil\n}","tryCatchPattern":"// On corrupt lock info, surface the path so operator can force-unlock\ninfo, err := state.lockInfo()\nif err != nil {\n    log.Printf(\"lock info unreadable: %v; consider terraform force-unlock\", err)\n    return err\n}","preventionTips":["Do not hand-edit .tflock files.","Investigate truncated lock files as possible disk or concurrency problems.","Use force-unlock only when no operation is actually running."],"tags":["state","locking","json","parse-error","local-backend"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}