hashicorp/terraform · error
failed to lock s3 state: %s
Error message
failed to lock s3 state: %s
What it means
Thrown by Backend.StateMgr (s3/backend_state.go:217) during first-time workspace init when client.Lock() fails. The underlying error is a *statemgr.LockError from the S3 native (.tflock) lock and/or the DynamoDB lock, wrapped here with %s.
Source
Thrown at internal/backend/remote-state/s3/backend_state.go:217
return nil, diags
}
exists := false
for _, s := range existing {
if s == name {
exists = true
break
}
}
// We need to create the object so it's listed by States.
if !exists {
// take a lock on this state while we write it
lockInfo := statemgr.NewLockInfo()
lockInfo.Operation = "init"
lockId, err := client.Lock(lockInfo)
if err != nil {
return nil, diags.Append(fmt.Errorf("failed to lock s3 state: %s", err))
}
// Local helper function so we can call it multiple places
lockUnlock := func(parent error) error {
if err := stateMgr.Unlock(lockId); err != nil {
return fmt.Errorf(strings.TrimSpace(errStateUnlock), lockId, err)
}
return parent
}
// Grab the value
// This is to ensure that no one beat us to writing a state between
// the `exists` check and taking the lock.
if err := stateMgr.RefreshState(); err != nil {
err = lockUnlock(err)
return nil, diags.Append(err)
}
View on GitHub (pinned to c9def3e214)
Solutions
- Run `terraform force-unlock <lock-id>` using the ID shown in the lock error info.
- Inspect and, if stale, manually delete the <key>.tflock object in S3 and/or the LockID row in DynamoDB.
- Confirm the dynamodb_table config points to an existing table and the role can read/write it.
- Coordinate with concurrent runs and retry once free.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check: is the state already locked before init?
// _, err := s3Client.HeadObject(ctx, &s3.HeadObjectInput{Bucket:&bucket, Key:aws.String(lockFilePath)})
// if err == nil { /* locked */ } Try / catch
// client.Lock returns *statemgr.LockError with Info about the holder
// _, err := client.Lock(info)
// var le *statemgr.LockError
// if errors.As(err, &le) {
// fmt.Printf("locked by %s; terraform force-unlock %s\n", le.Info.Who, le.Info.ID)
// } Prevention
- Always configure dynamodb_table or use_lock_file so locking is enforced.
- CI should acquire the workspace lock via a single coordinator.
- Clean up stale .tflock objects and DynamoDB rows after crashed runs.
When it happens
Trigger: Another run already holds the lock (S3 .tflock file exists, or DynamoDB LockID row present); DynamoDB ConditionalCheckFailedException; DynamoDB table missing/inaccessible; S3 PutObject for the lock file denied.
Common situations: Concurrent apply/plan on a brand-new workspace; a previous run crashed leaving a stale .tflock or DynamoDB row; misconfigured dynamodb_table name; IAM lacking the lock write permission.
Related errors
- failed to clean up file lock after DynamoDB lock error: %v;
- failed to unlock both S3 and DynamoDB: S3 error: %v, DynamoD
- invalid md5
- failed to lock inmem state: %s
- %v Additionally, unlocking the state in Kubernetes faile
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/e1253c22fd884403.
Report an issue: GitHub.