hashicorp/terraform · info · ErrNoState

ErrNoState

ErrNoState

Error message

no state

What it means

ErrNoState is a sentinel returned by statefile.Read when there is no state to read. Specifically it fires for two cases: a typed-nil *os.File was passed in, or the buffered source bytes are empty (len(src)==0). Callers are expected to detect it with errors.Is to treat 'no state yet' as a non-error (e.g. first run).

Source

Thrown at internal/states/statefile/read.go:21

package statefile

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"

	version "github.com/hashicorp/go-version"

	"github.com/hashicorp/terraform/internal/tfdiags"
	tfversion "github.com/hashicorp/terraform/version"
)

// ErrNoState is returned by ReadState when the state file is empty.
var ErrNoState = errors.New("no state")

// ErrUnusableState is an error wrapper to indicate that we *think* the input
// represents state data, but can't use it for some reason (as explained in the
// error text). Callers can check against this type with errors.As() if they
// need to distinguish between corrupt state and more fundamental problems like
// an empty file.
type ErrUnusableState struct {
	inner error
}

func errUnusable(err error) *ErrUnusableState {
	return &ErrUnusableState{inner: err}
}
func (e *ErrUnusableState) Error() string {
	return e.inner.Error()
}
func (e *ErrUnusableState) Unwrap() error {
	return e.inner

View on GitHub (pinned to c9def3e214)

Solutions

  1. If this is a fresh workspace, initialize state by running `terraform apply` (or `terraform init` for backends) — ErrNoState is expected and benign on first run.
  2. In code that calls statefile.Read, branch on errors.Is(err, statefile.ErrNoState) and treat it as an empty/initial state rather than a hard failure.
  3. If a state file unexpectedly became empty, restore from backend/backup (`terraform state push`) or version control.
  4. Ensure callers do not pass a typed-nil *os.File; pass a non-nil reader (e.g. bytes.Reader) when in doubt.

Example fix

// before
f, err := statefile.Read(r)
if err != nil {
    return err // wrongly fatal on first run
}
// after
f, err := statefile.Read(r)
if errors.Is(err, statefile.ErrNoState) {
    f = nil // fresh workspace, no state yet
} else if err != nil {
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect empty input before reading.
func isEmptyState(r io.Reader) (bool, error) {
    b, err := io.ReadAll(r)
    return len(b) == 0, err
}

Type guard

func isNoState(err error) bool {
    return errors.Is(err, statefile.ErrNoState)
}

Try / catch

f, err := statefile.Read(r)
if errors.Is(err, statefile.ErrNoState) {
    f = nil // first run / empty backend
} else if err != nil {
    return err
}

Prevention

When it happens

Trigger: Returned at internal/states/statefile/read.go:55 (typed-nil *os.File) and :74 (empty source). statemgr.filesystem (line 297, 475) and plans.planfile.reader wrap/compare against it; planfile wraps it in errUnusable(statefile.ErrNoState).

Common situations: First `terraform apply` in a fresh workspace (no terraform.tfstate yet). An empty state file touched but not written. A brand-new backend with no state object. A plan file referencing an empty tfstate. A typed-nil *os.File slipped through a code path that expected a real reader.

Related errors


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