hashicorp/terraform · error

Unknown or unexpected cost estimate state

Error message

Unknown or unexpected cost estimate state: %s

What it means

Default branch of the cost-estimate status switch. The TFE API returned a ce.Status value that does not match any known tfe.CostEstimate* constant. This indicates the server and client disagree on the set of possible statuses, almost always a version-skew problem.

Solutions

  1. Upgrade the Terraform CLI to a version at least as recent as the TFE/TFC deployment.
  2. If you cannot upgrade the CLI, pin TFE to a compatible version until the CLI can be updated.
  3. Report the unknown status string to Terraform maintainers if it persists on a current CLI.
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast if client and server versions are incompatible.
func ensureCompatible(tfeVersion, cliVersion string) error {
    if semverCompare(tfeVersion) > semverCompare(cliVersion) {
        return fmt.Errorf("TFE/TFC newer than CLI; upgrade Terraform to avoid unknown cost-estimate states")
    }
    return nil
}

Prevention

When it happens

Trigger: ce.Status is not Pending/Queued/Started/Finished/Errored/Canceled/SkippedDueToTargeting. Happens when a newer TFE/TFC introduces a cost-estimate status the running Terraform client does not recognize.

Common situations: TFE upgraded ahead of the Terraform CLI; using a very old CLI against a new TFC; beta/preview cost-estimation features emitting new statuses; mismatched terraform and tfe Go library versions in a custom build.

Related errors


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

Appendix: source

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

						" (%s elapsed)", current.Sub(started).Truncate(30*time.Second))
				}
				b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n"))
				b.CLI.Output(b.Colorize().Color("Waiting for cost estimate to complete..." + elapsed + "\n"))
			}
			continue
		case tfe.CostEstimateSkippedDueToTargeting:
			b.CLI.Output(b.Colorize().Color(msgPrefix + ":\n"))
			b.CLI.Output("Not available for this plan, because it was created with the -target option.")
			b.CLI.Output("\n------------------------------------------------------------------------")
			return nil
		case tfe.CostEstimateErrored:
			b.CLI.Output(msgPrefix + " errored.\n")
			b.CLI.Output("\n------------------------------------------------------------------------")
			return nil
		case tfe.CostEstimateCanceled:
			return fmt.Errorf("%s canceled.", msgPrefix)
		default:
			return fmt.Errorf("Unknown or unexpected cost estimate state: %s", ce.Status)
		}
	}
}

func (b *Remote) checkPolicy(stopCtx, cancelCtx context.Context, op *backendrun.Operation, r *tfe.Run) error {
	if b.CLI != nil {
		b.CLI.Output("\n------------------------------------------------------------------------\n")
	}
	for i, pc := range r.PolicyChecks {
		// Read the policy check logs. This is a blocking call that will only
		// return once the policy check is complete.
		logs, err := b.client.PolicyChecks.Logs(stopCtx, pc.ID)
		if err != nil {
			return generalError("Failed to retrieve policy check logs", err)
		}
		reader := bufio.NewReaderSize(logs, 64*1024)

		// Retrieve the policy check to get its current status.

View on GitHub (pinned to d32a084675)