{"record":{"id":"7f95aa0a2dfa3534","repo":"hashicorp/terraform","slug":"failed-to-get-existing-lock-file-w","errorCode":null,"errorMessage":"failed to get existing lock file: %w","messagePattern":"failed to get existing lock file: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/oci/client.go","lineNumber":312,"sourceCode":"\treturn info.ID, nil\n\n}\n\n// getLockInfo retrieves and parses a lock file from an oci bucket.\nfunc (c *RemoteClient) getLockInfo(ctx context.Context) (*statemgr.LockInfo, string, error) {\n\t// Attempt to retrieve the lock file from\n\tgetRequest := objectstorage.GetObjectRequest{\n\t\tNamespaceName: common.String(c.namespace),\n\t\tObjectName:    common.String(c.lockFilePath),\n\t\tBucketName:    common.String(c.bucketName),\n\t\tRequestMetadata: common.RequestMetadata{\n\t\t\tRetryPolicy: getDefaultRetryPolicy(),\n\t\t},\n\t}\n\n\tgetResponse, err := c.objectStorageClient.GetObject(ctx, getRequest)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"failed to get existing lock file: %w\", err)\n\t}\n\tlockByteData, err := io.ReadAll(getResponse.Content)\n\tif err != nil {\n\t\treturn nil, *getResponse.ETag, fmt.Errorf(\"failed to read existing lock file content: %w\", err)\n\t}\n\tlockInfo := &statemgr.LockInfo{}\n\tif err := json.Unmarshal(lockByteData, lockInfo); err != nil {\n\t\treturn lockInfo, \"\", fmt.Errorf(\"failed to unmarshal JSON data into LockInfo struct: %w\", err)\n\t}\n\treturn lockInfo, *getResponse.ETag, nil\n}\nfunc (c *RemoteClient) Unlock(id string) error {\n\tctx := context.TODO()\n\tlogger := logWithOperation(\"unlock-state-file\").Named(c.lockFilePath)\n\tlogger.Info(\"unlocking remote state\")\n\tlockInfo, etag, err := c.getLockInfo(ctx)\n\n\tif err != nil {","sourceCodeStart":294,"sourceCodeEnd":330,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote-state/oci/client.go#L294-L330","documentation":"getLockInfo's GetObject on the lockFilePath failed. Used in the Unlock flow (and as a secondary error joined into the Lock failure at client.go:284-287). The wrapped error is typically an OCI ServiceError (404, 403) or a transport error.","triggerScenarios":"Lock file already deleted by a concurrent Unlock or force-unlock (404); IAM lacks read on the lock object; transient network/5xx; the lock file never existed (Unlock called with no lock).","commonSituations":"Two clients unlocking at the same time; an operator ran force-unlock while automation also tried to unlock; permission revoked mid-run; calling Unlock on a workspace that was never locked.","solutions":["Confirm the lock file still exists at the expected lockFilePath before retrying.","Verify the principal has OBJECT_READ on the lock object (same bucket).","If the lock is already gone, the unlock is effectively complete — re-check workspace state rather than retrying blindly.","Read the wrapped error's HTTP status (via errors.As to common.ServiceError) to distinguish 404 from 403."],"exampleFix":"// before: blind Unlock that fails when the lock was already removed\nerr := client.Unlock(id)  // -> \"failed to get existing lock file: ... 404\"\n// after: tolerate 'already gone'\nerr := client.Unlock(id)\nif err != nil {\n    var se common.ServiceError\n    if errors.As(err, &se) && se.GetHTTPStatusCode() == 404 { err = nil }\n}","handlingStrategy":"retry","validationCode":"// Before Unlock, confirm the lock object still exists\nfunc lockPresent(c *RemoteClient, ctx context.Context) (bool, error) {\n    _, err := c.objectStorageClient.HeadObject(ctx, objectstorage.HeadObjectRequest{\n        NamespaceName: common.String(c.namespace),\n        BucketName:    common.String(c.bucketName),\n        ObjectName:    common.String(c.lockFilePath),\n    })\n    if err == nil { return true, nil }\n    var se common.ServiceError\n    if errors.As(err, &se) && se.GetHTTPStatusCode() == 404 { return false, nil }\n    return false, err\n}","typeGuard":"func httpStatusOf(err error) (int, bool) {\n    var se common.ServiceError\n    if errors.As(err, &se) { return se.GetHTTPStatusCode(), true }\n    return 0, false\n}","tryCatchPattern":"if _, err := c.objectStorageClient.GetObject(ctx, getRequest); err != nil {\n    var se common.ServiceError\n    if errors.As(err, &se) && se.GetHTTPStatusCode() == 404 {\n        // lock already gone; treat unlock as complete\n        return nil, \"\", errAlreadyUnlocked\n    }\n    return nil, \"\", fmt.Errorf(\"failed to get existing lock file: %w\", err)\n}","preventionTips":["Coordinate concurrent unlockers so only one attempts at a time.","Keep read permission on the lock object stable across the run.","Treat a missing lock file as success when no operation is active.","Log lock object existence before unlock to simplify diagnosis."],"tags":["oci","state-locking","object-storage"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}