opentofu/opentofu · error

The cloud backend does not support the %q operation.

Error message


The cloud backend does not support the %q operation.

What it means

Returned by Cloud.Operation (internal/cloud/backend.go:830) when the requested backend operation type is anything other than plan, apply, or refresh (refresh is rewritten to apply -refresh-state). The cloud backend simply has no implementation for other operation types, so it refuses with the %q-quoted op.Type.

Source

Thrown at internal/cloud/backend.go:830

	var f func(context.Context, context.Context, context.Context, *backend.Operation, *tfe.Workspace) (*tfe.Run, error)
	switch op.Type {
	case backend.OperationTypePlan:
		f = b.opPlan
	case backend.OperationTypeApply:
		f = b.opApply
	case backend.OperationTypeRefresh:
		// The `tofu refresh` command has been deprecated in favor of `tofu apply -refresh-state`.
		// Rather than respond with an error telling the user to run the other command we can just run
		// that command instead. We will tell the user what we are doing, and then do it.
		if b.View != nil {
			b.View.PreRefresh()
		}
		op.PlanMode = plans.RefreshOnlyMode
		op.PlanRefresh = true
		op.AutoApprove = true
		f = b.opApply
	default:
		return nil, fmt.Errorf(
			"\n\nThe cloud backend does not support the %q operation.", op.Type)
	}

	// Lock
	b.opLock.Lock()

	// Build our running operation
	// the runningCtx is only used to block until the operation returns.
	runningCtx, done := context.WithCancel(context.Background())
	runningOp := &backend.RunningOperation{
		Context:   runningCtx,
		PlanEmpty: true,
	}

	// stopCtx wraps the context passed in, and is used to signal a graceful Stop.
	stopCtx, stop := context.WithCancel(ctx)
	runningOp.Stop = stop

View on GitHub (pinned to 3561785c48)

Solutions

  1. Route the operation to the local backend (b.local.Operation) for non plan/apply/refresh types, as the cloud backend itself does for local-execution workspaces.
  2. If writing a tool, whitelist op.Type to backend.OperationTypePlan/Apply before handing the operation to the cloud backend.
  3. For refresh, keep using OperationTypeRefresh - it is supported via the apply/refresh-only rewrite, no code change needed.

Example fix

// before
runningOp, err := cloudBackend.Operation(ctx, op) // op.Type = OperationTypeValidate

// after
if op.Type != backend.OperationTypePlan && op.Type != backend.OperationTypeApply && op.Type != backend.OperationTypeRefresh {
    return localBackend.Operation(ctx, op)
}
runningOp, err := cloudBackend.Operation(ctx, op)
Defensive patterns

Strategy: validation

Validate before calling

// Only hand supported op types to the cloud backend
supported := map[backend.OperationType]bool{
    backend.OperationTypePlan: true,
    backend.OperationTypeApply: true,
    backend.OperationTypeRefresh: true,
}
if !supported[op.Type] {
    return local.Operation(ctx, op) // or reject earlier
}

Type guard

func cloudBackendSupports(t backend.OperationType) bool {
    switch t {
    case backend.OperationTypePlan, backend.OperationTypeApply, backend.OperationTypeRefresh:
        return true
    }
    return false
}

Try / catch

if _, err := cloud.Operation(ctx, op); err != nil {
    if strings.Contains(err.Error(), "does not support the") {
        // programming error on caller side: fix dispatch, do not retry
    }
}

Prevention

When it happens

Trigger: Calling backend Operation with op.Type set to console, validate, or any value the cloud package has no case for; programmatically reusing a local-backend operation struct against the cloud backend.

Common situations: Embedding code (tests, tools) that iterates all backend.OperationTypes; attempting `tofu console`/`tofu validate` semantics through the cloud backend instead of the local backend; drift between backend.OperationType constants and the cloud package's switch after a new type is added upstream.

Related errors


AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15). Data as JSON: /api/errors/9a367bc59efa5047. Report an issue: GitHub.