hashicorp/terraform · error

can't delete default state

Error message

can't delete default state

What it means

Thrown by Backend.DeleteWorkspace in the S3 state backend when name equals backend.DefaultStateName ("default") or "". Same guard as the pg backend: the default workspace is the canonical state and cannot be deleted through this API. The check runs before the remote client Delete() so no S3 object is touched.

Solutions

  1. Filter out 'default' and empty lines in any workspace-deletion loop.
  2. Use `tofu destroy` to clear default state instead of deleting the workspace.
  3. Validate workspace names from upstream config before calling delete.

Example fix

# before
for ws in $(tofu workspace list | tr -d '*'); do tofu workspace delete "$ws"; done
# after
for ws in $(tofu workspace list | tr -d '*' | grep -v '^default$'); do tofu workspace delete "$ws"; done
Defensive patterns

Strategy: validation

Validate before calling

func deleteWorkspace(name string) error {
    if name == backend.DefaultStateName || name == "" {
        return fmt.Errorf("protected workspace %q; use `tofu destroy` instead", name)
    }
    // ... proceed
}

Prevention

When it happens

Trigger: DeleteWorkspace(name, _) called with name == "default" or "". Triggered by `tofu workspace delete default` or any loop that tries to delete every workspace including default.

Common situations: Teardown script enumerating `tofu workspace list` and deleting each line; CI that auto-creates and deletes ephemeral workspaces but fails to skip default; misunderstanding default-state immutability.

Related errors


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

Appendix: source

Thrown at internal/backend/remote-state/s3/backend_state.go:142

	// not our key, so don't include it in our listing
	if parts[1] != b.keyName {
		return ""
	}

	return parts[0]
}

func (b *Backend) DeleteWorkspace(name string, _ bool) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics

	log := logger()
	log = logWithOperation(log, operationBackendDeleteWorkspace)
	log = log.With(
		logKeyBackendWorkspace, name,
	)

	if name == backend.DefaultStateName || name == "" {
		return diags.Append(fmt.Errorf("can't delete default state"))
	}

	log.Info("Deleting workspace")

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

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

// get a remote client configured for this state
func (b *Backend) remoteClient(name string) (*RemoteClient, error) {
	if name == "" {
		return nil, errors.New("missing state name")
	}

View on GitHub (pinned to d32a084675)