hashicorp/terraform · error

error uploading state: %w

Error message

error uploading state: %w

What it means

Wraps any failure of StateVersions.Upload (or the compatibility StateVersions.Create fallback) at the end of PersistState when uploading a new state version to HCP Terraform/TFE. On failure it sets s.stateUploadErr=true, which makes the subsequent Unlock a no-op so the workspace stays locked and no unsafe change is applied until a good state is uploaded.

Source

Thrown at internal/cloud/state.go:234

	stateFile, err := statefile.Read(bytes.NewReader(buf.Bytes()))
	if err != nil {
		return fmt.Errorf("failed to read state: %w", err)
	}

	ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues)
	if err != nil {
		return fmt.Errorf("failed to translate outputs: %w", err)
	}
	jsonStateOutputs, err := json.Marshal(ov)
	if err != nil {
		return fmt.Errorf("failed to marshal outputs to json: %w", err)
	}

	err = s.uploadState(s.lineage, s.serial, s.forcePush, buf.Bytes(), jsonState, jsonStateOutputs)
	if err != nil {
		s.stateUploadErr = true
		return fmt.Errorf("error uploading state: %w", err)
	}
	// After we've successfully persisted, what we just wrote is our new
	// reference state until someone calls RefreshState again.
	// We've potentially overwritten (via force) the state, lineage
	// and / or serial (and serial was incremented) so we copy over all
	// three fields so everything matches the new state and a subsequent
	// operation would correctly detect no changes to the lineage, serial or state.
	s.readState = s.state.DeepCopy()
	s.readLineage = s.lineage
	s.readSerial = s.serial

	return nil
}

// ShouldPersistIntermediateState implements statemgr.IntermediateStateConditionalPersister
func (s *State) ShouldPersistIntermediateState(info *statemgr.IntermediateStatePersistInfo) bool {
	if info.ForcePersist {
		return true

View on GitHub (pinned to c9def3e214)

Solutions

  1. Re-run the operation; Terraform re-locks and re-uploads and transient network/5xx errors usually clear.
  2. Verify the API token is valid: terraform logout && terraform login.
  3. Confirm no other concurrent run is writing to the workspace.
  4. Check the HCP Terraform / Terraform Enterprise status page and retry.
  5. If a serial/lineage conflict persists, confirm who holds the lock and use terraform force-unlock only when safe.

Example fix

# before: stale / expired token causes upload failure
terraform apply   # -> error uploading state: ...

# after: refresh credentials then retry
terraform logout
terraform login
terraform apply
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: token present and API reachable before attempting a write
if os.Getenv("TF_TOKEN_app_terraform_io") == "" && cfg.Token == "" {
    return errors.New("no API token configured; run terraform login")
}
// Optionally ping the current-state endpoint to confirm connectivity/auth
if _, err := client.StateVersions.ReadCurrent(ctx, workspace.ID); err != nil && !errors.Is(err, tfe.ErrResourceNotFound) {
    return fmt.Errorf("pre-upload check failed: %w", err)
}

Try / catch

err := state.PersistState(schemas)
if err != nil && (isRetryable(err) || isTransientHTTP(err)) {
    // re-lock and retry upload a bounded number of times
}
// NOTE: on failure the workspace is intentionally left locked; resolve the
// root cause or force-unlock deliberately rather than silently continuing.

Prevention

When it happens

Trigger: PersistState calls uploadState and the StateVersions.Upload API returns an error: network failure, 401/403 auth, 409 lineage/serial conflict, 5xx server error, or workspace state rejecting a new version.

Common situations: Transient network blips, expired or revoked API token, two concurrent runs racing on the same workspace (serial/lineage mismatch), or an HCP/TFE outage/rate-limit.

Related errors


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