hashicorp/terraform · error
Failed to retrieve lock information from OCI Object Storage
Error message
Failed to retrieve lock information from OCI Object Storage: %w
What it means
Unlock wraps any error returned by getLockInfo with this message (note the inconsistent capital 'Failed' versus sibling errors). It signals that lock information could not be retrieved before the delete was attempted. The wrapped %w is one of errors 310, 311, or 312 and should be inspected for the real cause.
Solutions
- Unwrap the error (errors.Unwrap / errors.As) to find the underlying 310/311/312 cause and address that specifically.
- For 404 underlying, the lock is already gone — treat the unlock as complete.
- For 412/unmarshal underlying, force-unlock by deleting the lock object after confirming no active operation.
- Run with TF_LOG=DEBUG to see the original getLockInfo error alongside this wrapper.
Example fix
// before: surface only the opaque wrapper
return err // "Failed to retrieve lock information..."
// after: unwrap to drive behavior
var se common.ServiceError
switch {
case errors.As(errors.Unwrap(err), &se) && se.GetHTTPStatusCode() == 404:
return nil // lock already gone
default:
return err
} Defensive patterns
Strategy: try-catch
Type guard
func underlyingStatus(err error) (int, bool) {
var se common.ServiceError
if errors.As(err, &se) { return se.GetHTTPStatusCode(), true }
return 0, false
} Try / catch
// Unwrap to drive behavior rather than treating the opaque wrapper
if err := c.Unlock(id); err != nil {
if status, ok := underlyingStatus(errors.Unwrap(err)); ok && status == 404 {
return nil // lock already gone
}
return err
} Prevention
- Always inspect the wrapped %w, not just this wrapper, before deciding recovery.
- Run with TF_LOG=DEBUG to capture the original getLockInfo error.
- Standardize error-message capitalization across the file (this one differs from siblings).
- Document the recovery path for each underlying cause (310/311/312).
When it happens
Trigger: Any getLockInfo failure during Unlock: 404/403 on the lock object (310), read-content stream failure (311), or JSON unmarshal failure (312).
Common situations: Recovery scenarios after a corrupted or manually edited lock; concurrent unlockers; permission or network issues during the unlock attempt.
Related errors
- Error unlocking oci state. Lock ID
- failed to get existing lock file
- failed to lock oci state
- failed to read existing lock file content
- failed to unmarshal JSON data into LockInfo struct
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/6bbc6e2a9a55006a.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/oci/client.go:331
}
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 {
return fmt.Errorf("Failed to retrieve lock information from OCI Object Storage: %w", err)
}
// Verify that the provided lock ID matches the lock ID of the retrieved lock file.
if lockInfo.ID != id {
return &statemgr.LockError{
Info: lockInfo,
Err: fmt.Errorf("lock ID '%s' does not match the existing lock ID '%s'", id, lockInfo.ID),
}
}
deleteRequest := objectstorage.DeleteObjectRequest{
NamespaceName: common.String(c.namespace),
ObjectName: common.String(c.lockFilePath),
BucketName: common.String(c.bucketName),
IfMatch: common.String(etag),
RequestMetadata: common.RequestMetadata{
RetryPolicy: getDefaultRetryPolicy(),
},
}View on GitHub (pinned to d32a084675)