{"record":{"id":"4ae4bbdb31ff0a50","repo":"hashicorp/terraform","slug":"discarded-using-the-ui-or-api","errorCode":null,"errorMessage":"discarded using the UI or API","messagePattern":"discarded using the UI or API","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote/backend_common.go","lineNumber":29,"sourceCode":"\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}\n\treturn time.Duration(backoff) * time.Millisecond","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/backend/remote/backend_common.go#L11-L47","documentation":"Sentinel returned by the remote backend's confirm() poller (backend_common.go:519,527). While the CLI waits at the apply/override prompt, it re-reads the run; if the run's status becomes tfe.RunDiscarded, someone discarded it through the TFC/HCP UI or API. Unlike errRunApproved this is a real abort: the run will not apply, and confirm() maps it to errApplyDiscarded (or errDestroyDiscarded in destroy mode) at backend_common.go:540-545.","triggerScenarios":"terraform apply/destroy against the remote backend with a pending-confirmation run whose status changes to tfe.RunDiscarded during the confirm() poll loop. Also returned for the 'override' keyword path (backend_common.go:519) when an override-needed run is discarded.","commonSituations":"A teammate clicks 'Discard run' in the Terraform Cloud UI while the CLI waits. Automation discards long-running speculative runs. A run expires or is auto-discarded by workspace settings while you are at the prompt.","solutions":["Re-run terraform apply/destroy to create a fresh run — the discarded run cannot be resumed.","Investigate who/what discarded the run (UI activity log or audit logs) if it was unexpected.","Adjust workspace auto-discard settings or permissions so runs are not discarded under you mid-confirmation."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isRunDiscarded(err error) bool {\n    return errors.Is(err, errRunDiscarded) || errors.Is(err, errApplyDiscarded) || errors.Is(err, errDestroyDiscarded)\n}","tryCatchPattern":"if err := b.confirm(ctx, op, opts, r, \"yes\"); err != nil {\n    if isRunDiscarded(err) {\n        // run was discarded server-side; must create a new run to proceed\n        return fmt.Errorf(\"run discarded externally: %w\", err)\n    }\n    return err\n}","preventionTips":["Do not discard runs in the UI while a CLI apply is pending.","Use errors.Is against the sentinel chain rather than string matching.","Re-run apply/destroy to start a fresh run after a discard."],"tags":["terraform","remote-backend","tfe","terraform-cloud","run","discard","sentinel"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}