hashicorp/terraform · error
failed to get existing lock file: %w
Error message
failed to get existing lock file: %w
What it means
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.
Source
Thrown at internal/backend/remote-state/oci/client.go:312
return info.ID, nil
}
// getLockInfo retrieves and parses a lock file from an oci bucket.
func (c *RemoteClient) getLockInfo(ctx context.Context) (*statemgr.LockInfo, string, error) {
// Attempt to retrieve the lock file from
getRequest := objectstorage.GetObjectRequest{
NamespaceName: common.String(c.namespace),
ObjectName: common.String(c.lockFilePath),
BucketName: common.String(c.bucketName),
RequestMetadata: common.RequestMetadata{
RetryPolicy: getDefaultRetryPolicy(),
},
}
getResponse, err := c.objectStorageClient.GetObject(ctx, getRequest)
if err != nil {
return nil, "", fmt.Errorf("failed to get existing lock file: %w", err)
}
lockByteData, err := io.ReadAll(getResponse.Content)
if err != nil {
return nil, *getResponse.ETag, fmt.Errorf("failed to read existing lock file content: %w", err)
}
lockInfo := &statemgr.LockInfo{}
if err := json.Unmarshal(lockByteData, lockInfo); err != nil {
return lockInfo, "", fmt.Errorf("failed to unmarshal JSON data into LockInfo struct: %w", err)
}
return lockInfo, *getResponse.ETag, nil
}
func (c *RemoteClient) Unlock(id string) error {
ctx := context.TODO()
logger := logWithOperation("unlock-state-file").Named(c.lockFilePath)
logger.Info("unlocking remote state")
lockInfo, etag, err := c.getLockInfo(ctx)
if err != nil {View on GitHub (pinned to d32a084675)
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.
Example fix
// before: blind Unlock that fails when the lock was already removed
err := client.Unlock(id) // -> "failed to get existing lock file: ... 404"
// after: tolerate 'already gone'
err := client.Unlock(id)
if err != nil {
var se common.ServiceError
if errors.As(err, &se) && se.GetHTTPStatusCode() == 404 { err = nil }
} Defensive patterns
Strategy: retry
Validate before calling
// Before Unlock, confirm the lock object still exists
func lockPresent(c *RemoteClient, ctx context.Context) (bool, error) {
_, err := c.objectStorageClient.HeadObject(ctx, objectstorage.HeadObjectRequest{
NamespaceName: common.String(c.namespace),
BucketName: common.String(c.bucketName),
ObjectName: common.String(c.lockFilePath),
})
if err == nil { return true, nil }
var se common.ServiceError
if errors.As(err, &se) && se.GetHTTPStatusCode() == 404 { return false, nil }
return false, err
} Type guard
func httpStatusOf(err error) (int, bool) {
var se common.ServiceError
if errors.As(err, &se) { return se.GetHTTPStatusCode(), true }
return 0, false
} Try / catch
if _, err := c.objectStorageClient.GetObject(ctx, getRequest); err != nil {
var se common.ServiceError
if errors.As(err, &se) && se.GetHTTPStatusCode() == 404 {
// lock already gone; treat unlock as complete
return nil, "", errAlreadyUnlocked
}
return nil, "", fmt.Errorf("failed to get existing lock file: %w", err)
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- failed to lock oci state: %s
- Error unlocking oci state. Lock ID: %s Error: %s You may h
- failed to access object '%s' in bucket '%s': %w
- failed to access object HttpStatusCode: %d OpcRequestId: %s
- unable to read 'content' from response: %w
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/7f95aa0a2dfa3534.
Report an issue: GitHub.