hashicorp/terraform · error
error deleting workspace
Error message
error deleting workspace %s: %v
What it means
Thrown by the Delete method when Workspaces.Delete (or Workspaces.SafeDelete) fails for any reason other than tfe.ErrResourceNotFound (which is silently ignored since the workspace is already gone). The error includes the workspace name. SafeDelete is used when the workspace supports it (Permissions.CanForceDelete is non-nil) and force is false; otherwise a hard Delete is performed.
Solutions
- Ensure all resources in the workspace are destroyed first (terraform destroy without -deletion) before workspace deletion
- Verify the authenticated identity has admin or delete-workspace permission on the workspace
- Check that no active run holds a lock on the workspace
- If SafeDelete is the issue, retry with force=true (equivalent to non-safe delete) if resource cleanup is confirmed
- Retry after transient TFE server errors
Defensive patterns
Strategy: validation
Validate before calling
// Before Delete, verify the workspace is empty and deletable:
ws, err := tfeClient.Workspaces.Read(ctx, organization, workspaceName)
if err != nil {
return err
}
if ws.Locked {
return fmt.Errorf("workspace %s is locked; cannot delete", workspaceName)
}
if ws.Permissions.CanForceDelete != nil && !*ws.Permissions.CanForceDelete {
return fmt.Errorf("workspace %s does not support safe delete and force was not requested", workspaceName)
} Try / catch
err := state.Delete(force)
if err != nil && strings.Contains(err.Error(), "error deleting workspace") {
if isRetryableError(err) {
time.Sleep(5 * time.Second)
return state.Delete(force)
}
// if SafeDelete failed, retry with force if the caller permits
if !force {
return state.Delete(true)
}
}
return err Prevention
- Destroy all resources (terraform destroy) before attempting workspace deletion
- Verify the service account has workspace-delete permission before running destroy with deletion
- Ensure no active run holds a lock on the workspace before deletion
When it happens
Trigger: SafeDelete fails because the workspace is not empty (has resources/state) and cannot be safely removed; the authenticated user lacks admin/delete permission on the workspace; the workspace is currently locked by an active run; network or TFE server error; attempting to delete a workspace that still has pending state version uploads.
Common situations: Running 'terraform destroy' with workspace deletion enabled on a workspace that still has unmanaged resources; CI service account without workspace-delete permission; workspace locked by a long-running apply; SafeDelete supported but workspace has non-empty resource count.
Related errors
- error deleting workspace
- could not read state version output
- could not read state version outputs
- error creating workspace
- error loading variables
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/4a1bd5d7ca01edb3.
Report an issue: GitHub.
Appendix: source
Thrown at internal/cloud/state.go:534
return lockErr
}
return nil
}
// Delete the remote state.
func (s *State) Delete(force bool) error {
var err error
isSafeDeleteSupported := s.workspace.Permissions.CanForceDelete != nil
if force || !isSafeDeleteSupported {
err = s.tfeClient.Workspaces.Delete(context.Background(), s.organization, s.workspace.Name)
} else {
err = s.tfeClient.Workspaces.SafeDelete(context.Background(), s.organization, s.workspace.Name)
}
if err != nil && err != tfe.ErrResourceNotFound {
return fmt.Errorf("error deleting workspace %s: %v", s.workspace.Name, err)
}
return nil
}
// GetRootOutputValues fetches output values from HCP Terraform
func (s *State) GetRootOutputValues(ctx context.Context) (map[string]*states.OutputValue, error) {
// The cloud backend initializes this value to true, but we want to implement
// some custom retry logic. This code presumes that the tfeClient doesn't need
// to be shared with other goroutines by the caller.
s.tfeClient.RetryServerErrors(false)
defer s.tfeClient.RetryServerErrors(true)
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
var so *tfe.StateVersionOutputsList
err := RetryBackoff(ctx, func() error {View on GitHub (pinned to d32a084675)