hashicorp/terraform · error

error uploading state in compatibility mode

Error message

error uploading state in compatibility mode: %v

What it means

Fallback path taken only when StateVersions.Upload returns ErrStateVersionUploadNotSupported (TFE v202306-1 and older which can't accept streamed uploads). The fallback inlines the base64 state into StateVersions.Create; if that legacy Create call fails, this error is returned and r.stateUploadErr is flagged so the workspace is NOT auto-unlocked.

Solutions

  1. Upgrade TFE to v202307-1 or later so the streamed Upload path is used (this fallback is a compatibility shim).
  2. If upgrade isn't possible, reduce state size (split resources, prune stale data) to stay under legacy inline limits.
  3. Verify the token has 'Write State' / admin permission on the workspace.
  4. Inspect the wrapped error for HTTP status — 409 serial conflicts need -force on the state push.
Defensive patterns

Strategy: fallback

Validate before calling

// Detect legacy server up front and warn
if _, err := c.StateVersions.Upload(ctx, wsID, opts); errors.Is(err, tfe.ErrStateVersionUploadNotSupported) {
    log.Println("TFE server is pre-v202307-1; falling back to inline upload")
}

Prevention

When it happens

Trigger: uploadStateFallback's StateVersions.Create returns an error on an old TFE server: legacy upload API rejected the payload (size limit, malformed MD5/lineage), 401/403 on state write, or the server errored mid-write.

Common situations: Old Terraform Enterprise deployment that hasn't been upgraded past v202306-1; force-push on a serial the legacy server rejects; very large state exceeding legacy inline-upload limits; token lacks 'Write State' permission.

Related errors


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

Appendix: source

Thrown at internal/backend/remote/backend_state.go:104

		Lineage:          tfe.String(stateFile.Lineage),
		Serial:           tfe.Int64(int64(stateFile.Serial)),
		MD5:              tfe.String(fmt.Sprintf("%x", md5.Sum(state))),
		Force:            tfe.Bool(r.forcePush),
		State:            tfe.String(base64.StdEncoding.EncodeToString(state)),
		JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(jsonStateOutputs)),
	}

	// If we have a run ID, make sure to add it to the options
	// so the state will be properly associated with the run.
	if r.runID != "" {
		options.Run = &tfe.Run{ID: r.runID}
	}

	// Create the new state.
	_, err := r.client.StateVersions.Create(ctx, r.workspace.ID, options)
	if err != nil {
		r.stateUploadErr = true
		return fmt.Errorf("error uploading state in compatibility mode: %v", err)
	}
	return err
}

// Put the remote state.
func (r *remoteClient) Put(state []byte) tfdiags.Diagnostics {
	var diags tfdiags.Diagnostics
	ctx := context.Background()

	// Read the raw state into a Terraform state.
	stateFile, err := statefile.Read(bytes.NewReader(state))
	if err != nil {
		return diags.Append(fmt.Errorf("error reading state: %s", err))
	}

	ov, err := jsonstate.MarshalOutputs(stateFile.State.RootOutputValues)
	if err != nil {
		return diags.Append(fmt.Errorf("error reading output values: %s", err))

View on GitHub (pinned to d32a084675)