Skip to content

State-sync bootstrap is structurally broken on ceremony chains: empty genesis validator set crashes seid (nil proposer) and wedges the node #457

Description

@bdchatham

Summary

A SeiNode with spec.fullNode.snapshot.stateSync on a ceremony chain (harbor ephemeral) crash-loops deterministically on first boot and can never recover — on any image. Reproduced on eng-yiming/yiming-migration-mock-4-rpc-1 (harbor), including a clean-room re-provision on a fresh PVC that failed on the very first boot before any snapshot discovery.

Error: error starting node: cannot load state: fromProto: validatorSet proposer error: nil validator

and on every subsequent restart, earlier:

Error: error creating node: LoadStateFromDBOrGenesisDocProvider(): fromProto: validatorSet proposer error: nil validator

Root cause (sei-tendermint)

  1. With state-sync enabled, OnStart skips the handshake/InitChain (sei-tendermint/node/node.go OnStart: "state sync will cover initialization the chain").
  2. A ceremony chain's assembled genesis has an empty tendermint validators: [] — validators materialize from gentxs at InitChain, which state-sync just skipped. Node construction has already saved the genesis-derived state (empty valset, nil proposer) to the state store.
  3. OnStart then reloads state (node.go ~L451: n.stateStore.Load()); the proto round-trip calls ValidatorFromProto(vp.GetProposer()) (types/validator_set.go ~L883) → nil proposer → fatal.
  4. Restarts fail earlier at LoadStateFromDBOrGenesisDocProvider (node.go ~L732) because the state store is now non-empty — i.e. a single failed first boot poisons the volume; recovery requires deleting the SeiNode + PVC.

Canonical chains are immune: their genesis carries the validator set (pacific-1 embeds 25 validators). Only ceremony chains (empty validators until InitChain) hit this — which is exactly the population the new spec.fullNode.snapshot.rpcServers field (spec-declared witnesses, replacing the canonical-syncer registry) exists to serve. As shipped, the feature green-lights a bootstrap that cannot succeed.

Timeline of the field case

  • Node requested stateSync; controller correctly gated on NoSyncersConfigured (chain has no registry entry).
  • Witnesses declared via rpcServers (harbor-engineering-workspace docs: LLD for SeiNode import-existing-storage feature #112): gate cleared, plan ran all tasks Complete, sidecar state-sync configure fetched a valid trust point (height 74523, hash verified) from the chain's own validators.
  • seid then crash-looped as above. Full teardown (SeiNode + cascaded PVC) + Flux re-provision reproduced the identical crash on boot 1 of a pristine volume with zero state-sync progress lines.
  • Workaround: drop stateSync, block-sync from genesis (harbor-engineering-workspace feat: SeiNode import existing PVC + ensure-data-pvc create-path fix #113).

Suggested fixes (layered)

  1. sei-tendermint: when state-sync will bootstrap the chain, don't persist (or don't reload via the strict fromProto path) the genesis-derived empty-valset state — or make an empty validator set survive the proto round-trip until state-sync installs the real one. This is the root fix.
  2. Controller: the StateSyncReady gate should refuse (or warn in the condition) when the target chain's genesis carries an empty validator set — i.e. any ceremony chain — until (1) lands. Today the gate validates witness count, then hands the node a guaranteed wedge.
  3. Secondary bug: a first-boot state-sync failure of any kind leaves an unloadable state DB with no auto-recovery; the sidecar could detect this exact error signature and reset the tendermint state rather than requiring PVC deletion.
  4. Runbook: troubleshooting-seinode.md (harbor-dev skill) has no entry for this signature; add symptom → cause → recovery once the behavior is settled.

Also observed

The pod existed (Pending on the missing PVC) for the entire 3.5h the plan was gated — the running harbor controller applies the STS before the gate clears. origin/main's "hold STS while gate blocks plan" (#456 era) fixes this; noting for deploy prioritization.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions