{"record":{"id":"5a25850c10d266cb","repo":"hashicorp/terraform","slug":"lock-file-s-exists","errorCode":null,"errorMessage":"lock file %s exists","messagePattern":"lock file (.+?) exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/cos/client.go","lineNumber":98,"sourceCode":"}\n\n// Lock lock remote state file for writing\nfunc (c *remoteClient) Lock(info *statemgr.LockInfo) (string, error) {\n\tlog.Printf(\"[DEBUG] lock remote state file %s\", c.lockFile)\n\n\terr := c.cosLock(c.bucket, c.lockFile)\n\tif err != nil {\n\t\treturn \"\", c.lockError(err)\n\t}\n\tdefer c.cosUnlock(c.bucket, c.lockFile)\n\n\texists, _, _, err := c.getObject(c.lockFile)\n\tif err != nil {\n\t\treturn \"\", c.lockError(err)\n\t}\n\n\tif exists {\n\t\treturn \"\", c.lockError(fmt.Errorf(\"lock file %s exists\", c.lockFile))\n\t}\n\n\tinfo.Path = c.lockFile\n\tdata, err := json.Marshal(info)\n\tif err != nil {\n\t\treturn \"\", c.lockError(err)\n\t}\n\n\tcheck := fmt.Sprintf(\"%x\", md5.Sum(data))\n\terr = c.putObject(c.lockFile, data)\n\tif err != nil {\n\t\treturn \"\", c.lockError(err)\n\t}\n\n\treturn check, nil\n}\n\n// Unlock unlock remote state file","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/cos/client.go#L80-L116","documentation":"remoteClient.Lock first calls cosLock (a COS-side guard), then checks whether the lock info object already exists at c.lockFile via getObject. If a prior run left a lock info object there, exists==true and the lock is rejected. This means another run (live or stale) is holding the state lock.","triggerScenarios":"Lock() -> cosLock succeeds -> getObject(c.lockFile) returns exists==true.","commonSituations":"Another Terraform run currently holds the lock; a previous run crashed and left a stale lock object; the lock object was written by an older Terraform version and never cleaned up.","solutions":["Confirm no active run, then `terraform force-unlock <LOCK_ID>` (the ID is the md5 stored on the lock object).","If force-unlock can't reach it, manually delete the lock object from COS at c.lockFile.","Coordinate team access so only one run targets the workspace at a time."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-check whether a COS lock object is present before attempting Lock.\nfunc lockFileExists(c *remoteClient) (bool, error) {\n    exists, _, _, err := c.getObject(c.lockFile)\n    if err != nil {\n        return false, err\n    }\n    return exists, nil\n}\n\n// if exists, prompt for force-unlock instead of letting Lock fail.","typeGuard":null,"tryCatchPattern":"// On lock failure, classify and offer force-unlock.\nid, err := c.Lock(info)\nif err != nil {\n    var le *statemgr.LockError\n    if errors.As(err, &le) {\n        return fmt.Errorf(\"state already locked (id=%s); run terraform force-unlock %s\", le.Info.ID, le.Info.ID)\n    }\n    return err\n}","preventionTips":["Coordinate team access so only one run targets each workspace.","Always let Terraform clean up its lock; don't kill -9 without force-unlock.","Wire terraform force-unlock into CI recovery steps.","Monitor the bucket for stale lock objects."],"tags":["cos","tencent-cloud","state-locking","backend"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}