opentofu/opentofu · error
can't show a saved cloud plan unless the current root module
Error message
can't show a saved cloud plan unless the current root module is connected to Terraform Cloud
What it means
getDataFromCloudPlan runs when the file passed to tofu show is a saved cloud plan bookmark (cloudplan.SavedPlanBookmark, containing a run ID and hostname) rather than a local plan. It requires the backend resolved from the current configuration to be *cloud.Cloud, i.e. the root module must be connected to Terraform Cloud via the cloud block. If the current configuration resolves to any other backend, the plan JSON cannot be fetched and this error is returned.
Source
Thrown at internal/command/show.go:402
plan, stateFile, config, err = getDataFromPlanfileReader(ctx, lp, rootCall)
} else if cp, ok := pf.Cloud(); ok {
redacted := c.viewType != arguments.ViewJSON
jsonPlan, err = c.getDataFromCloudPlan(ctx, cp, redacted, enc)
}
return plan, jsonPlan, stateFile, config, err
}
func (c *ShowCommand) getDataFromCloudPlan(ctx context.Context, plan *cloudplan.SavedPlanBookmark, redacted bool, enc encryption.Encryption) (*cloudplan.RemotePlanJSON, error) {
// Set up the backend
b, backendDiags := c.Backend(ctx, nil, enc.State())
if backendDiags.HasErrors() {
return nil, errUnusable(backendDiags.Err(), "cloud plan")
}
// Cloud plans only work if we're cloud.
cl, ok := b.(*cloud.Cloud)
if !ok {
return nil, errUnusable(fmt.Errorf("can't show a saved cloud plan unless the current root module is connected to Terraform Cloud"), "cloud plan")
}
result, err := cl.ShowPlanForRun(context.Background(), plan.RunID, plan.Hostname, redacted)
if err != nil {
err = errUnusable(err, "cloud plan")
}
return result, err
}
// maybeGetSchemas is a thin wrapper around [Meta.MaybeGetSchemas] that
// takes a [*statefile.File] instead of a [*states.State] and tolerates
// the state file being nil, since that's more convenient for the
// "tofu show" methods that may or may not have a state file to use.
func (c *ShowCommand) maybeGetSchemas(ctx context.Context, stateFile *statefile.File, config *configs.Config) (*tofu.Schemas, tfdiags.Diagnostics) {
ctx, span := tracing.Tracer().Start(ctx, "Get Schemas")
defer span.End()
if stateFile == nil {View on GitHub (pinned to 3561785c48)
Solutions
- Restore the cloud block configuration that produced the plan and run tofu init, then retry tofu show <plan>
- View the plan in the Terraform Cloud UI for the run ID embedded in the bookmark instead of locally
- Regenerate the plan locally with the current backend if cloud access is not intended
- Confirm you are in the same root module directory that created the plan file
Example fix
# before
# plan saved under cloud backend, cloud block now commented out
tofu show tfplan # can't show a saved cloud plan
# after
terraform {
cloud {
organization = "my-org"
workspaces { name = "ws" }
}
}
# then: tofu init && tofu show tfplan Defensive patterns
Strategy: validation
Validate before calling
raw, err := os.ReadFile(planPath)
if err != nil {
return err
}
if bytes.Contains(raw, []byte("run_id")) {
// saved cloud plan bookmark: tofu show needs the cloud{} backend active,
// otherwise it fails with the 'connected to Terraform Cloud' error
if !cloudBackendActive() {
return errors.New("plan is a cloud bookmark; restore the cloud block or view it in TFC")
}
} Type guard
func isCloudPlanBookmark(raw []byte) bool {
var probe struct {
CloudPlan *struct {
RunID string `json:"run_id"`
Hostname string `json:"hostname"`
} `json:"cloud_plan"`
}
return json.Unmarshal(raw, &probe) == nil && probe.CloudPlan != nil
} Try / catch
result, err := getDataFromCloudPlan(ctx, bookmark, redacted, enc)
if err != nil {
if strings.Contains(err.Error(), "connected to Terraform Cloud") {
// backend mismatch: point the user at the TFC run instead of retrying
return fmt.Errorf("open the plan at https://%s/app/.../runs/%s", bookmark.Hostname, bookmark.RunID)
}
return err
} Prevention
- Keep the cloud block in the configuration until saved plan files have been consumed
- Prefer tofu show -json <local-plan> artifacts produced by the same backend configuration
- Document which backend produced each shared plan file
When it happens
Trigger: Running tofu show <plan-file> where the file was written by a run driven by the cloud backend, while the current root module has no cloud block or uses a backend block; commenting out the cloud block between saving and showing the plan; running show from a different directory whose backend differs.
Common situations: Viewing a cloud-run plan artifact on a laptop checkout that uses a local or S3 backend; mid-migration off Terraform Cloud; plan files shared between machines or branches with different backend configs.
Related errors
- error: using a saved cloud plan when executing OpenTofu loca
- error selecting workspace: %w
- failed to load state manager: %w
- failed to load state: %w
- Error checking remote OpenTofu version
AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15).
Data as JSON: /api/errors/5d12331ead96456a.
Report an issue: GitHub.