hashicorp/terraform · error

is not a valid state name

Error message

%q is not a valid state name

What it means

Returned by the backend's client factory when the requested workspace name is the empty string. The factory refuses to construct a remoteClient for an empty name because it would resolve to an ambiguous state file path.

Solutions

  1. Ensure the workspace name passed into the operation is non-empty before calling backend methods.
  2. Default to backend.DefaultStateName when no workspace is selected.
  3. Check the caller (e.g., terraform workspace selection) and fix the empty-name source.

Example fix

// before
name := os.Getenv("TF_WORKSPACE")  // empty when unset
c, err := b.client(name)
// after
name := os.Getenv("TF_WORKSPACE")
if name == "" { name = backend.DefaultStateName }
c, err := b.client(name)
Defensive patterns

Strategy: validation

Validate before calling

name := os.Getenv("TF_WORKSPACE")
if name == "" {
    name = backend.DefaultStateName
}
if name == "" {
    return fmt.Errorf("workspace name is empty")
}

Type guard

func isValidWorkspaceName(name string) bool { return name != "" }

Prevention

When it happens

Trigger: client(name) is called with name == "" — typically because a workspace lookup returned empty or an upstream caller passed an unset variable.

Common situations: A script computes a workspace name from an env var that is unset; programmatic use of the backend passes an empty string; workspace name came from a malformed config map key.

Related errors


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

Appendix: source

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

// 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,
		bucketName:    b.bucketName,
		stateFilePath: b.stateFile(name),
		lockFilePath:  b.lockFile(name),
		encryptionKey: b.encryptionKey,
		kmsKeyName:    b.kmsKeyName,
	}, nil
}

// StateMgr reads and returns the named state from GCS. If the named state does
// not yet exist, a new state file is created.
func (b *Backend) StateMgr(name string) (statemgr.Full, tfdiags.Diagnostics) {
	var diags tfdiags.Diagnostics

	c, err := b.client(name)

View on GitHub (pinned to d32a084675)