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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions