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

  1. Confirm the lock file still exists at the expected lockFilePath before retrying.
  2. Verify the principal has OBJECT_READ on the lock object (same bucket).
  3. If the lock is already gone, the unlock is effectively complete — re-check workspace state rather than retrying blindly.
  4. 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

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


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/7f95aa0a2dfa3534. Report an issue: GitHub.