hashicorp/terraform · error

invalid syntax

Error message

invalid syntax: %w

What it means

First-line parse failure in ParseBackendStateFile: json.Unmarshal of the version-sniff struct failed, meaning the backend state file (.terraform/terraform.tfstate) is not valid JSON at all. The %w carries the json decoder's offset/reason.

Solutions

  1. Open .terraform/terraform.tfstate and check it parses as JSON (jq . < file).
  2. Restore from version control or backup if corrupted.
  3. If the file is empty/garbage, delete it and run `terraform init` to regenerate.
  4. Re-save as UTF-8 without BOM and Unix line endings.

Example fix

# diagnose
jq . .terraform/terraform.tfstate

# regenerate if unrecoverable
rm .terraform/terraform.tfstate
terraform init
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: confirm the file is valid JSON before handing it to the parser
if !json.Valid(src) {
    return nil, fmt.Errorf("backend state file is not valid JSON; re-run `terraform init`")
}

Type guard

func looksLikeBackendState(src []byte) bool {
    var sniff struct{ Version int `json:"version"` }
    return json.Unmarshal(src, &sniff) == nil && sniff.Version > 0
}

Try / catch

f, err := ParseBackendStateFile(src)
if err != nil {
    if !json.Valid(src) {
        return nil, errors.New("backend state file corrupt; delete it and re-run `terraform init`")
    }
    return nil, err
}

Prevention

When it happens

Trigger: Pointing Terraform at a backend state file that is empty, truncated, binary (e.g. legacy gob format), or contains a syntax error (trailing comma, unquoted key, BOM, etc.).

Common situations: File corrupted by a crashed write, edited by hand with a typo, replaced by a non-JSON file (e.g. a Terraform state snapshot or a log), or transferred with encoding damage (CRLF/BOM on Windows).

Related errors


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

Appendix: source

Thrown at internal/command/workdir/backend_state.go:82

// Returns an error if the content is not valid syntax, or if the file is
// of an unsupported format version.
//
// This does not immediately decode the embedded backend config, and so
// it's possible that a subsequent call to [BackendConfigState.Config] will
// return further errors even if this call succeeds.
func ParseBackendStateFile(src []byte) (*BackendStateFile, error) {
	// To avoid any weird collisions with as-yet-unknown future versions of
	// the format, we'll do a first pass of decoding just the "version"
	// property, and then decode the rest only if we find the version number
	// that we're expecting.
	type VersionSniff struct {
		Version   int    `json:"version"`
		TFVersion string `json:"terraform_version,omitempty"`
	}
	var versionSniff VersionSniff
	err := json.Unmarshal(src, &versionSniff)
	if err != nil {
		return nil, fmt.Errorf("invalid syntax: %w", err)
	}
	if versionSniff.Version == 0 {
		// This could either mean that it's explicitly "version": 0 or that
		// the version property is missing. We'll assume the latter here
		// because state snapshot version 0 was an encoding/gob binary format
		// rather than a JSON format and so it would be very weird for
		// that to show up in a JSON file.
		return nil, fmt.Errorf("invalid syntax: no format version number")
	}
	if versionSniff.Version != 3 {
		return nil, fmt.Errorf("unsupported backend state version %d; you may need to use Terraform CLI v%s to work in this directory", versionSniff.Version, versionSniff.TFVersion)
	}

	// If we get here then we can be sure that this file at least _thinks_
	// it's format version 3.
	var stateFile BackendStateFile
	err = json.Unmarshal(src, &stateFile)
	if err != nil {

View on GitHub (pinned to d32a084675)