{"record":{"id":"01f20e2a479f36fe","repo":"hashicorp/terraform","slug":"getting-lock-info-got-an-error-v","errorCode":null,"errorMessage":"\ngetting lock info got an error: %#v","messagePattern":"\ngetting lock info got an error: %#v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/oss/client.go","lineNumber":198,"sourceCode":"\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\n\t}\n\n\tgetParams := &tablestore.SingleRowQueryCriteria{","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/oss/client.go#L180-L216","documentation":"Thrown as a secondary error (joined via errors.Join) while handling a failed PutRow in RemoteClient.Lock: after the lock write failed, the backend tried to best-effort fetch the existing lock info via getLockInfo, and that lookup also failed. The user gets both the PutRow failure and the lock-info retrieval failure.","triggerScenarios":"PutRow failed AND getLockInfo failed in sequence — e.g., OTS endpoint went fully unreachable mid-call, credentials lack both PutRow and GetRow, or the table was deleted between the two calls.","commonSituations":"Total OTS outage or permission failure affecting both write and read of the lock table; the lock row exists but GetRow is denied; network partition from runner to OTS.","solutions":["Treat the primary PutRow error as the root cause; resolving OTS reachability/permissions resolves both.","Verify credentials have both ots:PutRow and ots:GetRow on the table.","Check OTS endpoint health and security-group/NAT egress.","Retry once OTS is healthy; if a stale lock is suspected, manually inspect the table."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// When PutRow fails, attempt getLockInfo but tolerate its failure and report both errors.\n_, err := c.otsClient.PutRow(putReq)\nif err != nil {\n    putErr := fmt.Errorf(\"invoking PutRow got an error: %#v\", err)\n    info, infoErr := c.getLockInfo()\n    if infoErr != nil {\n        err = errors.Join(putErr, fmt.Errorf(\"\\ngetting lock info got an error: %#v\", infoErr))\n    } else { err = putErr }\n    return \"\", &statemgr.LockError{Err: err, Info: info}\n}","preventionTips":["Provision OTS permissions (PutRow + GetRow) as a unit so the secondary lookup rarely fails.","Monitor OTS endpoint health; this double-error usually signals total OTS trouble.","When this chained error appears, resolve OTS reachability first, then re-attempt the lock."],"tags":["alibaba-cloud","tablestore","ots","state-lock","concurrency","error-chaining"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}