hashicorp/terraform · critical
%v Additionally, unlocking the state in Kubernetes faile
Error message
%v Additionally, unlocking the state in Kubernetes failed: Error message: %q Lock ID (gen): %v Secret Name: %v You may have to force-unlock this state in order to use it again. The Kubernetes backend acquires a lock during initialization to ensure the initial state file is created.
What it means
During initial state creation the Kubernetes backend acquires a lock; if a subsequent WriteState/PersistState (or the final cleanup unlock) fails AND the compensating Unlock also fails, both errors are concatenated into this multi-line message. It tells the user the state may be left locked and points at force-unlock as the recovery path.
Source
Thrown at internal/backend/remote-state/kubernetes/backend_state.go:128
secretName, err := c.createSecretName(0)
if err != nil {
return nil, diags.Append(err)
}
// Local helper function so we can call it multiple places
unlock := func(baseErr error) error {
if err := stateMgr.Unlock(lockID); err != nil {
const unlockErrMsg = `%v
Additionally, unlocking the state in Kubernetes failed:
Error message: %q
Lock ID (gen): %v
Secret Name: %v
You may have to force-unlock this state in order to use it again.
The Kubernetes backend acquires a lock during initialization to ensure
the initial state file is created.`
return fmt.Errorf(unlockErrMsg, baseErr, err.Error(), lockID, secretName)
}
return baseErr
}
if err := stateMgr.WriteState(states.NewState()); err != nil {
unlockErr := unlock(err)
return nil, diags.Append(unlockErr)
}
if err := stateMgr.PersistState(nil); err != nil {
unlockErr := unlock(err)
return nil, diags.Append(unlockErr)
}
// Unlock, the state should now be initialized
if err := unlock(nil); err != nil {
return nil, diags.Append(err)
}View on GitHub (pinned to c9def3e214)
Solutions
- Run `terraform force-unlock <Lock ID (gen)>` using the Lock ID printed in the message to release the stale lock.
- Resolve the underlying base error shown at the top of the message (e.g. RBAC/namespace/quota issues for Secret writes).
- Verify the lease/secret names shown exist and are not held by another active run before force-unlocking.
- Re-run `terraform init`/`terraform plan` once the lock is cleared and the base error is fixed.
Example fix
# before - initial state write failed and left the state locked terraform init # fails with the composite unlock error # after - release the stale lock, then re-run terraform force-unlock <Lock-ID-from-message> terraform init
Defensive patterns
Strategy: try-catch
Try / catch
// After a failed init that may leave a k8s lock, parse the lock id and force-unlock
_, diags := b.StateMgr(name)
if diags.HasErrors() {
if lockID := extractLockID(diags.Err().Error()); lockID != "" {
// surface to user / run: terraform force-unlock <lockID>
}
return diags
} Prevention
- Ensure the backend's service account has stable Secret/Lease create+update+get RBAC.
- Avoid concurrent `terraform init` against the same fresh workspace.
- Keep a runbook entry mapping this message to `terraform force-unlock <id>`.
When it happens
Trigger: StateMgr() (backend_state.go:100-148) finds no existing state, takes an init lock, then WriteState or PersistState fails (e.g. secret create/update API error); the deferred unlock() also fails (e.g. lease update conflict), so unlock() returns this composite error wrapping the original baseErr.
Common situations: Kubernetes API transient errors during the secret write for the initial empty state (RBAC, quota, etcd unavailable); the lease object was modified concurrently so the unlock Update hits a conflict; the lease was deleted out-of-band so the unlock's getLease/update fails.
Related errors
- lock id %q does not match existing lock
- state is already unlocked
- failed to lock inmem state: %s
- Failed to configure: %s
- secret_suffix must not end with '-<number>', got %q
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/2c59f4ec3e6841d5.
Report an issue: GitHub.