opentofu/opentofu · error

The "remote" backend does not support the %q operation.

Error message


The "remote" backend does not support the %q operation.

What it means

The default branch of the operation dispatch switch in Remote.Operation (backend.go:711): op.Type is something other than Plan, Apply, or Refresh (refresh is special-cased with its own message). This is a programming-error path — the CLI itself never routes other operation types here, so embedders or custom frontends calling backend.Operation with an unexpected OperationType hit it.

Source

Thrown at internal/backend/remote/backend.go:711

		return b.local.Operation(ctx, op)
	}

	// Set the remote workspace name.
	op.Workspace = w.Name

	// Determine the function to call for our operation
	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:
		return nil, fmt.Errorf(
			"\n\nThe \"refresh\" operation is not supported when using the \"remote\" backend. " +
				"Use \"tofu apply -refresh-only\" instead.")
	default:
		return nil, fmt.Errorf(
			"\n\nThe \"remote\" 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. Set op.Type explicitly to backend.OperationTypePlan or OperationTypeApply before calling Operation
  2. If you maintain a caller of backend.Operation, validate/whitelist the operation type before dispatch
  3. For refresh-style needs use OperationTypePlan/Apply with refresh-only semantics instead
  4. Return a clear error to your own users rather than forwarding unknown types into the backend

Example fix

// before
op := &backend.Operation{Type: backend.OperationType(0)} // zero/unknown
_, err := b.Operation(ctx, op) // -> "does not support the \"%!z(...)\" operation."

// after
op := &backend.Operation{Type: backend.OperationTypePlan}
_, err := b.Operation(ctx, op)
Defensive patterns

Strategy: validation

Validate before calling

// whitelist operation types before calling backend.Operation
func validOpType(t backend.OperationType) bool {
  switch t {
  case backend.OperationTypePlan, backend.OperationTypeApply, backend.OperationTypeRefresh:
    return true
  }
  return false
}
if !validOpType(op.Type) {
  return fmt.Errorf("unsupported operation type: %d", op.Type)
}

Type guard

func isUnsupportedOpErr(err error) bool {
  return err != nil && strings.Contains(err.Error(), "does not support the") && strings.Contains(err.Error(), "operation")
}

Try / catch

// treat as a programmer error: fail fast with the caller's own context
if _, err := b.Operation(ctx, op); err != nil {
  if isUnsupportedOpErr(err) {
    return fmt.Errorf("wrapper bug: op.Type=%d rejected by remote backend: %w", op.Type, err)
  }
  return err
}

Prevention

When it happens

Trigger: A Go program obtains a backend.Remote and calls Operation with an op.Type value outside the handled set (custom or newly added operation type constants), or a zero-value Operation struct whose Type was never set.

Common situations: Tooling built on the backend package that forwards user-supplied operation strings; refactors where a new OperationType constant is introduced without updating the remote backend switch; unit tests constructing bare Operation values.

Related errors


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