{"record":{"id":"d134d5c7678a5a06","repo":"hashicorp/terraform","slug":"invoking-putrow-got-an-error-v","errorCode":null,"errorMessage":"invoking PutRow got an error: %#v","messagePattern":"invoking PutRow got an error: %#v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/oss/client.go","lineNumber":195,"sourceCode":"\t\t},\n\t\tColumns: []tablestore.AttributeColumn{\n\t\t\t{\n\t\t\t\tColumnName: \"Info\",\n\t\t\t\tValue:      string(info.Marshal()),\n\t\t\t},\n\t\t},\n\t\tCondition: &tablestore.RowCondition{\n\t\t\tRowExistenceExpectation: tablestore.RowExistenceExpectation_EXPECT_NOT_EXIST,\n\t\t},\n\t}\n\n\tlog.Printf(\"[DEBUG] Recording state lock in tablestore: %#v; LOCKID:%s\", putParams, c.lockPath())\n\n\t_, err := c.otsClient.PutRow(&tablestore.PutRowRequest{\n\t\tPutRowChange: putParams,\n\t})\n\tif err != nil {\n\t\terr = fmt.Errorf(\"invoking PutRow got an error: %#v\", err)\n\t\tlockInfo, infoErr := c.getLockInfo()\n\t\tif infoErr != nil {\n\t\t\terr = errors.Join(err, fmt.Errorf(\"\\ngetting lock info got an error: %#v\", infoErr))\n\t\t}\n\t\tlockErr := &statemgr.LockError{\n\t\t\tErr:  err,\n\t\t\tInfo: lockInfo,\n\t\t}\n\t\tlog.Printf(\"[ERROR] state lock error: %s\", lockErr.Error())\n\t\treturn \"\", lockErr\n\t}\n\n\treturn info.ID, nil\n}\n\nfunc (c *RemoteClient) getMD5() ([]byte, error) {\n\tif c.otsTable == \"\" {\n\t\treturn nil, nil","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/oss/client.go#L177-L213","documentation":"Thrown by RemoteClient.Lock when the OTS PutRow that records the lock row fails. Because lock acquisition is conditional (RowExistenceExpectation_EXPECT_NOT_EXIST), the most common cause is a conflicting existing lock. The error is wrapped, lockInfo is best-effort fetched, and a statemgr.LockError carrying both is returned.","triggerScenarios":"OTS PutRow on the lock table returns an error: a row already exists for this state's lock path (someone else holds the lock), the table is missing, credentials lack ots:PutRow, or OTS is unreachable.","commonSituations":"Concurrent terraform runs against the same workspace; a prior crashed run left a stale lock; OTS permission/endpoint problems.","solutions":["Read the lockInfo returned with the error to identify who holds the lock (use 'terraform force-unlock <ID>' if stale).","Ensure no other run is active against the workspace.","Verify OTS table and ots:PutRow permission.","Retry after force-unlock or once the other run completes."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// On PutRow failure, surface the existing lock info so operators can force-unlock.\n_, err := c.otsClient.PutRow(putReq)\nif err != nil {\n    info, _ := c.getLockInfo()\n    return \"\", &statemgr.LockError{Err: err, Info: info}\n}","preventionTips":["Serialize terraform runs per workspace.","Always force-unlock after a crashed run.","Confirm ots:PutRow and ots:GetRow permissions together."],"tags":["alibaba-cloud","tablestore","ots","state-lock","concurrency","force-unlock"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}