{"record":{"id":"a6cd89cac32d3f36","repo":"hashicorp/terraform","slug":"lock-info-id-id-path-pa","errorCode":null,"errorMessage":"Lock Info:\n  ID:        {{.ID}}\n  Path:      {{.Path}}\n  Operation: {{.Operation}}\n  Who:       {{.Who}}\n  Version:   {{.Version}}\n  Created:   {{.Created}}\n  Info:      {{.Info}}","messagePattern":"Lock Info:\n  ID:        (.+?)\\}\n  Path:      (.+?)\\}\n  Operation: (.+?)\\}\n  Who:       (.+?)\\}\n  Version:   (.+?)\\}\n  Created:   (.+?)\\}\n  Info:      (.+?)\\}","errorType":"exception","errorClass":"LockInfo","httpStatus":null,"severity":"error","filePath":"internal/states/statemgr/locker.go","lineNumber":176,"sourceCode":"\t// don't error out on user and hostname, as we don't require them\n\tuserName := \"\"\n\tif userInfo, err := user.Current(); err == nil {\n\t\tuserName = userInfo.Username\n\t}\n\thost, _ := os.Hostname()\n\n\tinfo := &LockInfo{\n\t\tID:      id,\n\t\tWho:     fmt.Sprintf(\"%s@%s\", userName, host),\n\t\tVersion: version.Version,\n\t\tCreated: time.Now().UTC(),\n\t}\n\treturn info\n}\n\n// Err returns the lock info formatted in an error\nfunc (l *LockInfo) Err() error {\n\treturn errors.New(l.String())\n}\n\n// Marshal returns a string json representation of the LockInfo\nfunc (l *LockInfo) Marshal() []byte {\n\tjs, err := json.Marshal(l)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn js\n}\n\n// String return a multi-line string representation of LockInfo\nfunc (l *LockInfo) String() string {\n\ttmpl := `Lock Info:\n  ID:        {{.ID}}\n  Path:      {{.Path}}\n  Operation: {{.Operation}}\n  Who:       {{.Who}}","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/states/statemgr/locker.go#L158-L194","documentation":"This is the multi-line template rendered by LockInfo.String() and returned (via LockInfo.Err()) when state-lock acquisition fails because another process holds the lock. The template emits ID, Path, Operation, Who (user@host), Version, Created, and Info of the competing lock holder so the user knows who/what is blocking them. The placeholders {{.ID}} etc. are filled from the LockError.Info captured by the failed Lock call.","triggerScenarios":"A Locker.Lock call returns a *LockError whose Info is the existing lock's metadata; LockWithContext or the CLI formats it via LockInfo.String()/Err() to present the conflict to the user.","commonSituations":"Two Terraform runs (apply/plan/destroy) targeting the same state concurrently; a previous run crashed without releasing the lock; a CI job and a developer running locally against the same backend; a long-running apply still in progress.","solutions":["Wait for the other operation to finish, then retry — LockWithContext already backs off and retries until the context deadline.","If the other holder is genuinely dead (crashed machine), use `terraform force-unlock <ID>` with the ID shown in the rendered lock info.","Verify the Who/Path fields to confirm the holder is not your own stale process before force-unlocking.","Do NOT force-unlock during an active cooperative run — it can corrupt state."],"exampleFix":"// before\nid, err := statemgr.LockWithContext(ctx, mgr, info)\nif err != nil {\n    return err // shows raw template-ish error\n}\n\n// after\nid, err := statemgr.LockWithContext(ctx, mgr, info)\nif err != nil {\n    var le *statemgr.LockError\n    if errors.As(err, &le) && le.Info != nil {\n        return fmt.Errorf(\"state is locked by another run:\\n%s\\nif that run is gone, run: terraform force-unlock %s\", le.Info.String(), le.Info.ID)\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"n/a — lock conflict depends on remote state; pre-check by attempting Lock.","typeGuard":"func isLockError(err error) (*statemgr.LockError, bool) {\n    var le *statemgr.LockError\n    if errors.As(err, &le) {\n        return le, true\n    }\n    return nil, false\n}","tryCatchPattern":"id, err := statemgr.LockWithContext(ctx, mgr, info)\nif le, ok := isLockError(err); ok && le.Info != nil {\n    return fmt.Errorf(\"state locked by %s@%s (id %s); run `terraform force-unlock %s` if stale\", le.Info.Who, le.Info.Path, le.Info.ID, le.Info.ID)\n}","preventionTips":["Use LockWithContext with a sensible timeout so transient locks clear automatically.","Only force-unlock when you are certain the holder is dead.","Serialize CI and local runs against the same workspace to avoid lock contention."],"tags":["state-lock","concurrency","lock-conflict","terraform"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}