{"record":{"id":"419fcd76a3b7e590","repo":"hashicorp/terraform","slug":"failed-to-lock-cos-state-s","errorCode":null,"errorMessage":"Failed to lock cos state: %s","messagePattern":"Failed to lock cos state: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/cos/backend_state.go","lineNumber":115,"sourceCode":"\t}\n\n\texists := false\n\tfor _, candidate := range ws {\n\t\tif candidate == name {\n\t\t\texists = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !exists {\n\t\tlog.Printf(\"[DEBUG] workspace %v not exists\", name)\n\n\t\t// take a lock on this state while we write it\n\t\tlockInfo := statemgr.NewLockInfo()\n\t\tlockInfo.Operation = \"init\"\n\t\tlockId, err := c.Lock(lockInfo)\n\t\tif err != nil {\n\t\t\treturn nil, diags.Append(fmt.Errorf(\"Failed to lock cos state: %s\", err))\n\t\t}\n\n\t\t// Local helper function so we can call it multiple places\n\t\tlockUnlock := func(e error) error {\n\t\t\tif err := stateMgr.Unlock(lockId); err != nil {\n\t\t\t\treturn fmt.Errorf(unlockErrMsg, err, lockId)\n\t\t\t}\n\t\t\treturn e\n\t\t}\n\n\t\t// Grab the value\n\t\tif err := stateMgr.RefreshState(); err != nil {\n\t\t\terr = lockUnlock(err)\n\t\t\treturn nil, diags.Append(err)\n\t\t}\n\n\t\t// If we have no state, we have to create an empty state\n\t\tif v := stateMgr.State(); v == nil {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/cos/backend_state.go#L97-L133","documentation":"Mirror of error 180 for the COS backend. During StateMgr init for a workspace that does not yet exist, the backend acquires a lock in order to atomically create an empty state. If remoteClient.Lock returns an error, it is wrapped here.","triggerScenarios":"Backend.StateMgr(name) where name is not in Workspaces(); c.Lock(lockInfo) returns an error — typically cosLock fails, the lock file already exists, or COS putObject of the lock info fails.","commonSituations":"A stale lock object in the COS bucket from a previous crashed run; a concurrent Terraform run holding the lock; COS credentials lack PutObject on the lock file key; network/COS 5xx.","solutions":["Capture the Lock ID (md5) from the wrapped error and run `terraform force-unlock <LOCK_ID>`.","Inspect the lock object in the bucket at the lock file path; remove it manually if force-unlock cannot reach it.","Verify COS credentials have PutObject/GetObject/DeleteObject on both state and lock key prefixes.","Avoid concurrent runs against the same workspace."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-check whether a COS lock object already exists for the workspace.\nfunc cosLockExists(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}","typeGuard":null,"tryCatchPattern":"// Catch the lock failure, surface the lock ID, offer force-unlock.\nsm, diags := backend.StateMgr(name)\nif diags.HasErrors() {\n    msg := diags.Err().Error()\n    if strings.Contains(msg, \"Failed to lock cos state\") {\n        if id := extractLockID(msg); id != \"\" {\n            return fmt.Errorf(\"state %q locked (id=%s); run terraform force-unlock %s\", name, id, id)\n        }\n    }\n    return diags.Err()\n}","preventionTips":["Serialize Terraform runs per workspace via CI concurrency limits.","Verify COS credentials have Get/Put/Delete on both state and lock key prefixes.","Wire terraform force-unlock into the runbook for crashed runs.","Monitor the COS bucket for orphaned lock files."],"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"}