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.innerView on GitHub (pinned to c9def3e214)
Solutions
- 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.
- 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.
- If a state file unexpectedly became empty, restore from backend/backup (`terraform state push`) or version control.
- 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
- Always branch on errors.Is(err, statefile.ErrNoState); never treat it as fatal.
- Avoid passing typed-nil *os.File; guard with `if f, ok := r.(*os.File); ok && f == nil`.
- Initialize state explicitly on fresh workspaces.
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
- Apply discarded.
- approved using the UI or API
- discarded using the UI or API
- overridden using the UI or API
- No state file was found! State management commands require
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/e81b3232a3edf8ae.
Report an issue: GitHub.