hashicorp/terraform · info

approved using the UI or API

Error message

approved using the UI or API

What it means

This is a sentinel returned by the remote backend's confirmation poller (confirm() in backend_common.go:529). When the CLI is waiting for the user to type 'yes' to apply, it periodically re-reads the run; if the run is no longer confirmable (r.Actions.IsConfirmable == false) and was not discarded, it means someone approved the run through the Terraform Cloud / Enterprise web UI or the TFE API. It is informational, not a hard failure: backend_apply.go:237,242 explicitly checks `err != errRunApproved` and uses it to skip the CLI-side Runs.Apply call because approval already happened server-side.

Solutions

  1. Do nothing — the run is proceeding on the server; the CLI message is a notification, not a failure.
  2. If you must own approval from the CLI, ensure no other operator/automation approves the run concurrently and disable conflicting auto-apply for that workspace.
  3. Configure the workspace so only CLI-driven runs are used, or avoid running apply from two places at once.
Defensive patterns

Strategy: try-catch

Type guard

// Detect the 'approved externally' sentinel from the remote backend.
func isRunApprovedExternally(err error) bool {
    return errors.Is(err, errRunApproved) // compare to the exported sentinel if exposed, else string-match
}

Try / catch

err := b.confirm(ctx, op, opts, r, "yes")
if err != nil {
    if errors.Is(err, errRunApproved) {
        // Run was approved via UI/API; no local Apply call needed. Treat as success.
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling terraform apply against the 'remote' backend while a workspace run is in the pending-confirmation state, and the run transitions to approved by another channel before the CLI confirms. Specifically the confirm() goroutine (backend_common.go:524-531) returns errRunApproved when keyword=="yes", !r.Actions.IsConfirmable, and r.Status != tfe.RunDiscarded.

Common situations: A teammate clicks 'Confirm & Apply' in the Terraform Cloud UI while your local CLI sits at the apply prompt. An automation script approves the run via POST /runs/:id/actions/apply while the CLI polls. A workspace with mixed approval sources where Auto-apply races the manual prompt.

Related errors


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

Appendix: source

Thrown at internal/backend/remote/backend_common.go:28

	"fmt"
	"io"
	"math"
	"strconv"
	"strings"
	"time"

	tfe "github.com/hashicorp/go-tfe"

	"github.com/hashicorp/terraform/internal/backend/backendrun"
	"github.com/hashicorp/terraform/internal/logging"
	"github.com/hashicorp/terraform/internal/plans"
	"github.com/hashicorp/terraform/internal/terraform"
)

var (
	errApplyDiscarded   = errors.New("Apply discarded.")
	errDestroyDiscarded = errors.New("Destroy discarded.")
	errRunApproved      = errors.New("approved using the UI or API")
	errRunDiscarded     = errors.New("discarded using the UI or API")
	errRunOverridden    = errors.New("overridden using the UI or API")
)

var (
	backoffMin = 1000.0
	backoffMax = 3000.0

	runPollInterval = 3 * time.Second
)

// backoff will perform exponential backoff based on the iteration and
// limited by the provided min and max (in milliseconds) durations.
func backoff(min, max float64, iter int) time.Duration {
	backoff := math.Pow(2, float64(iter)/5) * min
	if backoff > max {
		backoff = max
	}

View on GitHub (pinned to d32a084675)