hashicorp/terraform · error
failed to read existing lock file content: %w
Error message
failed to read existing lock file content: %w
What it means
Raised in getLockInfo after GetObject for the lock file succeeded but io.ReadAll(getResponse.Content) failed while reading the lock file body. It is the lock-file analogue of error 303: the HTTP call worked, the body stream did not. The ETag is still captured (316 dereferences *getResponse.ETag) for caller use.
Source
Thrown at internal/backend/remote-state/oci/client.go:316
// 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 {
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 {View on GitHub (pinned to c9def3e214)
Solutions
- Check errors.Is for context.Canceled vs an io/net reset to distinguish user action from network.
- Re-run Unlock/Lock; lock files are small and the retry usually succeeds.
- If persistent, inspect the network path to object storage (proxy/NAT resetting connections).
Defensive patterns
Strategy: retry
Validate before calling
if ctx.Err() != nil { return nil, "", ctx.Err() } // do not read body on a cancelled ctx Type guard
if errors.Is(err, context.Canceled) || errors.Is(err, io.ErrUnexpectedEOF) { /* classify */ } Try / catch
lockByteData, err := io.ReadAll(getResponse.Content)
if err != nil {
return nil, *getResponse.ETag, fmt.Errorf("failed to read existing lock file content: %w", err)
} Prevention
- Keep connectivity stable for the (tiny) lock file read.
- Retry transient resets.
- Avoid cancelling the context during Unlock.
When it happens
Trigger: io.ReadAll on getResponse.Content (client.go:314) returns an error: connection reset mid-stream, io.ErrUnexpectedEOF, or context cancellation while draining the (typically tiny) lock file body.
Common situations: Network reset while reading the lock file; context cancelled during Unlock/Lock; proxy truncating a small response. Rare for such a small object but possible on an unstable link.
Related errors
- unable to read 'content' from response: %w
- failed to access object '%s' in bucket '%s': %w
- failed to unmarshal JSON data into LockInfo struct: %w
- Failed to retrieve lock information from OCI Object Storage:
- lock ID '%s' does not match the existing lock ID '%s'
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/3cf40f658fbd5e72.
Report an issue: GitHub.