hashicorp/terraform · error
Failed to delete state file
Error message
Failed to delete state file %v: %v
What it means
Thrown by the remote client Delete when c.stateFile().Delete(ctx) fails. Delete is used by DeleteWorkspace to remove a workspace's state object.
Solutions
- Grant roles/storage.objectAdmin which includes objects.delete.
- Confirm the workspace still has a state object; idempotently tolerate 'not found'.
- Check bucket retention policy if deletes are being rejected.
- Retry transient errors.
Example fix
// before — no delete permission // after gsutil iam ch serviceAccount:terraform@proj.iam.gserviceaccount.com:roles/storage.objectAdmin gs://tf-state
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: confirm objects.delete permission. // Minimal test: gsutil rm gs://bucket/.delprobe
Try / catch
// Make delete idempotent for 'not found'.
if err := f.Delete(ctx); err != nil && !errors.Is(err, storage.ErrObjectNotExist) {
return err
} Prevention
- Grant roles/storage.objectAdmin (includes delete).
- Make deletion automation tolerate 'already gone'.
- Check bucket retention policy if deletes are rejected.
When it happens
Trigger: stateFile().Delete returns an error — missing storage.objects.delete permission, object does not exist (though typically that is tolerated by the API), precondition failure, or transport error.
Common situations: Service account has objectAdmin minus delete; attempting to delete a workspace whose state was already removed; object held by a retention policy; transient failures.
Related errors
- Failed to open state file at
- Failed to upload state to
- querying Cloud Storage failed
- Failed to read state file attrs from
- Failed to read state file from
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/bf973df4d8b9f952.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/gcs/client.go:88
}
if _, err := stateFileWriter.Write(data); err != nil {
return err
}
return stateFileWriter.Close()
}()
if err != nil {
return diags.Append(fmt.Errorf("Failed to upload state to %v: %v", c.stateFileURL(), err))
}
return diags
}
func (c *remoteClient) Delete() tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
ctx := context.TODO()
if err := c.stateFile().Delete(ctx); err != nil {
return diags.Append(fmt.Errorf("Failed to delete state file %v: %v", c.stateFileURL(), err))
}
return diags
}
// Lock writes to a lock file, ensuring file creation. Returns the generation
// number, which must be passed to Unlock().
func (c *remoteClient) Lock(info *statemgr.LockInfo) (string, error) {
ctx := context.TODO()
// update the path we're using
// we can't set the ID until the info is written
info.Path = c.lockFileURL()
infoJson, err := json.Marshal(info)
if err != nil {
return "", err
}View on GitHub (pinned to d32a084675)