hashicorp/terraform · error
ErrInvalidRemotePlanFormat
ErrInvalidRemotePlanFormat
Error message
invalid remote plan format, must be 1
What it means
Exported sentinel ErrInvalidRemotePlanFormat (cloudplan/saved_plan.go:13, code=ErrInvalidRemotePlanFormat). When reading a saved-plan bookmark file, StateRead/Read unmarshals JSON and checks bookmark.RemotePlanFormat == 1; any other value (0 from an empty/garbage file, or a future incompatible format) returns this error. As the comments at saved_plan.go:50-54 note, these errors imply the file probably is not a saved plan bookmark at all.
Source
Thrown at internal/cloud/cloudplan/saved_plan.go:13
// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package cloudplan
import (
"encoding/json"
"errors"
"io"
"os"
"strings"
)
var ErrInvalidRemotePlanFormat = errors.New("invalid remote plan format, must be 1")
var ErrInvalidRunID = errors.New("invalid run ID")
var ErrInvalidHostname = errors.New("invalid hostname")
type SavedPlanBookmark struct {
RemotePlanFormat int `json:"remote_plan_format"`
RunID string `json:"run_id"`
Hostname string `json:"hostname"`
}
func NewSavedPlanBookmark(runID, hostname string) SavedPlanBookmark {
return SavedPlanBookmark{
RemotePlanFormat: 1,
RunID: runID,
Hostname: hostname,
}
}
func LoadSavedPlanBookmark(filepath string) (SavedPlanBookmark, error) {View on GitHub (pinned to d32a084675)
Solutions
- Re-generate the plan file with `terraform plan -out=<file>` using the same backend, then pass it to terraform show/apply.
- Confirm you are using the cloud/remote backend for both the plan and the show/apply step.
- If the file is hand-edited or from another tool, treat it as not-a-plan and regenerate it.
Defensive patterns
Strategy: validation
Validate before calling
// Peek at the bookmark before handing it to StateRead.
func isValidRemoteBookmark(b cloudplan.SavedPlanBookmark) error {
if b.RemotePlanFormat != 1 { return cloudplan.ErrInvalidRemotePlanFormat }
if b.Hostname == "" { return cloudplan.ErrInvalidHostname }
if b.RunID == "" || !strings.HasPrefix(b.RunID, "run-") { return cloudplan.ErrInvalidRunID }
return nil
} Type guard
func isRemotePlanBookmark(data []byte) bool {
var b cloudplan.SavedPlanBookmark
if err := json.Unmarshal(data, &b); err != nil { return false }
return b.RemotePlanFormat == 1
} Try / catch
b, err := cloudplan.StateRead(path)
if err != nil {
if errors.Is(err, cloudplan.ErrInvalidRemotePlanFormat) {
// not a cloud plan file; try local plan path or re-plan
}
} Prevention
- Generate plan files with the same backend you'll show/apply with.
- Don't reuse plan files across Terraform versions.
- Use errors.Is against the exported sentinels.
When it happens
Trigger: terraform show/apply pointing at a plan file (e.g. `terraform show foo.tfplan`) where foo.tfplan is not a cloud saved-plan bookmark — e.g. a local plan, a corrupt file, or unrelated bytes — so RemotePlanFormat != 1 after json.Unmarshal.
Common situations: Mixing up a local-backend plan file with a cloud-backend bookmark. Passing a stale plan from a different Terraform version. A truncated/empty file producing a zero-value struct.
Related errors
- ErrInvalidRunID
- tag object values must be strings
- tag elements must be strings
- at most 1 action can be invoked per operation
- Cannot set both 'source' and 'content'
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/c8f410bbb7d8bae3.
Report an issue: GitHub.