{"record":{"id":"6d0087245c8b219b","repo":"hashicorp/terraform","slug":"error-unmarshaling-lock-info-s","errorCode":null,"errorMessage":"error unmarshaling lock info: %s","messagePattern":"error unmarshaling lock info: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/consul/client.go","lineNumber":356,"sourceCode":"\t}, nil)\n\n\treturn err\n}\n\nfunc (c *RemoteClient) getLockInfo() (*statemgr.LockInfo, error) {\n\tpath := c.lockPath() + lockInfoSuffix\n\tpair, _, err := c.Client.KV().Get(path, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pair == nil {\n\t\treturn nil, nil\n\t}\n\n\tli := &statemgr.LockInfo{}\n\terr = json.Unmarshal(pair.Value, li)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error unmarshaling lock info: %s\", err)\n\t}\n\n\treturn li, nil\n}\n\nfunc (c *RemoteClient) Lock(info *statemgr.LockInfo) (string, error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif !c.lockState {\n\t\treturn \"\", nil\n\t}\n\n\tc.info = info\n\n\t// These checks only are to ensure we strictly follow the specification.\n\t// Terraform shouldn't ever re-lock, so provide errors for the 2 possible\n\t// states if this is called.","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/backend/remote-state/consul/client.go#L338-L374","documentation":"Raised by getLockInfo() (consul/client.go:356) when the JSON blob stored at <lockPath>/.lockinfo cannot be unmarshaled into statemgr.LockInfo. The lock-info key is meant to be the JSON-serialized LockInfo written by putLockInfo; corruption or schema drift makes unmarshal fail and the wrapped error is surfaced.","triggerScenarios":"getLockInfo() reads pair.Value for the .lockinfo key and json.Unmarshal returns an error. Encountered when the value is truncated, non-JSON, or written by an incompatible terraform version with a different LockInfo schema.","commonSituations":"Manual edit/deletion of the .lockinfo KV; a crashed run left a partial write; mixing terraform versions where LockInfo fields differ; someone wrote arbitrary bytes to that key.","solutions":["Read the raw value of <lockPath>/.lockinfo in Consul to see what was actually stored.","If it is stale or corrupt, clear the lock with `terraform force-unlock <lock-id>` (the session ID).","As a last resort, delete the .lockinfo KV and the matching .lock KV manually, then re-run.","Standardize the terraform version across the team to avoid LockInfo schema drift."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Gracefully handle corrupt lock info by clearing the stale lock\nif _, err := client.getLockInfo(); err != nil {\n    if strings.Contains(err.Error(), \"error unmarshaling lock info\") {\n        // clear the corrupt .lockinfo and .lock KVs, then force-unlock\n        clearConsulLock(client)\n    }\n}","preventionTips":["Do not manually write to the .lockinfo KV path.","Use a consistent terraform version across the team to avoid LockInfo schema drift.","Prefer `terraform force-unlock` over manually deleting lock keys."],"tags":["consul","remote-state","lock","json","corruption","lockinfo"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}