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

  1. Unwrap the error (errors.Unwrap / errors.As) to find the underlying 310/311/312 cause and address that specifically.
  2. For 404 underlying, the lock is already gone — treat the unlock as complete.
  3. For 412/unmarshal underlying, force-unlock by deleting the lock object after confirming no active operation.
  4. 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

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


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)