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
- Upgrade the Terraform CLI to a version at least as recent as the TFE/TFC deployment.
- If you cannot upgrade the CLI, pin TFE to a compatible version until the CLI can be updated.
- 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
- Keep the Terraform CLI version >= the TFE/TFC version to avoid unrecognized statuses.
- Pin versions in lockstep in CI matrices.
- When TFE is upgraded, schedule a CLI upgrade in the same change window.
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
- Unknown or unexpected policy state
- canceled.
- Error asking
- Error creating workspace
- error finding remote workspace
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)