hashicorp/terraform · error

cowardly refusing to delete the

Error message

cowardly refusing to delete the %q state

What it means

Returned by DeleteWorkspace when the requested workspace name equals backend.DefaultStateName ("default"). The default state is the primary state and is protected from deletion regardless of the force flag.

Solutions

  1. Skip the default workspace in any deletion loop: filter out name == "default".
  2. Do not attempt `terraform workspace delete default`; the default workspace is non-deletable.
  3. If you truly need to clear it, manually delete state objects in GCS rather than using DeleteWorkspace.

Example fix

// before
for ws in $(terraform workspace list); do terraform workspace delete "$ws"; done
// after
for ws in $(terraform workspace list); do [ "$ws" = "default" ] && continue; terraform workspace delete "$ws"; done
Defensive patterns

Strategy: validation

Validate before calling

// Never call DeleteWorkspace for the default state.
const defaultName = "default"
if name == defaultName {
    return nil // or log and skip
}
return backend.DeleteWorkspace(name, force)

Type guard

func isDefaultWorkspace(name string) bool { return name == backend.DefaultStateName }

Prevention

When it happens

Trigger: DeleteWorkspace is invoked with name == backend.DefaultStateName ("default"). The force argument is ignored — the guard is unconditional.

Common situations: An automation script iterates all workspaces and tries to delete each, including default; a user runs `terraform workspace delete default`; tooling that wraps `terraform workspace list` and deletes every entry.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/c0e46ef4c8268fcf. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/gcs/backend_state.go:69

		if !strings.HasSuffix(name, stateFileSuffix) {
			continue
		}
		st := strings.TrimSuffix(name, stateFileSuffix)

		if st != backend.DefaultStateName {
			states = append(states, st)
		}
	}

	sort.Strings(states[1:])
	return states, diags
}

// DeleteWorkspace deletes the named workspaces. The "default" state cannot be deleted.
func (b *Backend) DeleteWorkspace(name string, _ bool) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics
	if name == backend.DefaultStateName {
		return diags.Append(fmt.Errorf("cowardly refusing to delete the %q state", name))
	}

	c, err := b.client(name)
	if err != nil {
		return diags.Append(err)
	}

	return diags.Append(c.Delete())
}

// client returns a remoteClient for the named state.
func (b *Backend) client(name string) (*remoteClient, error) {
	if name == "" {
		return nil, fmt.Errorf("%q is not a valid state name", name)
	}

	return &remoteClient{
		storageClient: b.storageClient,

View on GitHub (pinned to d32a084675)