{"record":{"id":"01b22dcc6e05a298","repo":"hashicorp/terraform","slug":"approved-using-the-ui-or-api","errorCode":null,"errorMessage":"approved using the UI or API","messagePattern":"approved using the UI or API","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"internal/backend/remote/backend_common.go","lineNumber":28,"sourceCode":"\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\ttfe \"github.com/hashicorp/go-tfe\"\n\n\t\"github.com/hashicorp/terraform/internal/backend/backendrun\"\n\t\"github.com/hashicorp/terraform/internal/logging\"\n\t\"github.com/hashicorp/terraform/internal/plans\"\n\t\"github.com/hashicorp/terraform/internal/terraform\"\n)\n\nvar (\n\terrApplyDiscarded   = errors.New(\"Apply discarded.\")\n\terrDestroyDiscarded = errors.New(\"Destroy discarded.\")\n\terrRunApproved      = errors.New(\"approved using the UI or API\")\n\terrRunDiscarded     = errors.New(\"discarded using the UI or API\")\n\terrRunOverridden    = errors.New(\"overridden using the UI or API\")\n)\n\nvar (\n\tbackoffMin = 1000.0\n\tbackoffMax = 3000.0\n\n\trunPollInterval = 3 * time.Second\n)\n\n// backoff will perform exponential backoff based on the iteration and\n// limited by the provided min and max (in milliseconds) durations.\nfunc backoff(min, max float64, iter int) time.Duration {\n\tbackoff := math.Pow(2, float64(iter)/5) * min\n\tif backoff > max {\n\t\tbackoff = max\n\t}","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote/backend_common.go#L10-L46","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do nothing — the run is proceeding on the server; the CLI message is a notification, not a failure.","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.","Configure the workspace so only CLI-driven runs are used, or avoid running apply from two places at once."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"// Detect the 'approved externally' sentinel from the remote backend.\nfunc isRunApprovedExternally(err error) bool {\n    return errors.Is(err, errRunApproved) // compare to the exported sentinel if exposed, else string-match\n}","tryCatchPattern":"err := b.confirm(ctx, op, opts, r, \"yes\")\nif err != nil {\n    if errors.Is(err, errRunApproved) {\n        // Run was approved via UI/API; no local Apply call needed. Treat as success.\n        return nil\n    }\n    return err\n}","preventionTips":["Avoid approving the same run from both the CLI and the UI/API concurrently.","Branch on the sentinel with errors.Is rather than string comparison.","Treat errRunApproved as informational, not a failure."],"tags":["terraform","remote-backend","tfe","terraform-cloud","run","apply","sentinel"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}