Skip to content

History Publish (Testnet): validator dies during catchup — bucket entry XDR parse failure in restore_from_has_parallel (disk_bucket.rs:228) #3941

Description

@tomerweller

Symptom

The History Publish (Testnet) job Test History Publish now fast-fails (~38–82 s) by the validator process dying during catchup, distinct from the checkpoint-publish timeout/runner-cancel mode tracked by #3741. The node downloads all buckets, begins restore_from_has_parallel (disk-backed mode), loads levels 0–10, then aborts parsing a bucket entry:

INFO  henyey_history::catchup::buckets: apply_buckets START — RSS 166 MB
INFO  henyey_history::catchup::buckets: Applying buckets to build state at ledger 4378879 (disk-backed mode)
...
INFO  henyey_bucket::bucket_list: restore_from_has_parallel: loaded level curr+snap level=10 curr_entries=5774175 snap_entries=0 elapsed_ms=6718
ERROR henyey_app::run_cmd: Node stopped with error error=catchup failed: Failed to restore live bucket list:
      bucket serialization error: Failed to parse bucket entry at offset 4483092: xdr value invalid
Error: catchup failed: Failed to restore live bucket list: bucket serialization error:
      Failed to parse bucket entry at offset 4483092: xdr value invalid
##[error]Process completed with exit code 1.

Evidence

Run Created Duration Conclusion
33169000459 2026-08-28T11:55:26Z 38 s failure (bucket-parse death, confirmed above)
33067408027 2026-08-27T11:26:55Z 82 s failure (fast-fail; log expired, same mode by duration)
  • Head sha: 129190c0 (current origin/main HEAD) — NOT the deployed validator binary 0ac84d42.
  • Each daily run catches up to the then-current testnet checkpoint (here target_ledger=4378879), i.e. a different bucket set each run. A parse failure recurring across days on different checkpoints argues a systematic deserialization defect, not a one-off corrupt download.
  • The older Aug-25/26 History Publish failures were ~180–246 s (the History Publish (Testnet): checkpoint-publish timeout persists on a35b1066 despite #3732 #3741 stall/timeout mode), so at least two distinct failure modes are now present in this workflow.

Code path (file:line)

  • crates/bucket/src/disk_bucket.rs:228stellar_xdr::BucketEntry::from_xdr(record.body, Limits::none()) returns Errcrates/bucket/src/disk_bucket.rs:232-235 formats Failed to parse bucket entry at offset {}: {} (BucketError::Serialization).
  • Record framing comes from self.records.next_record() (disk_bucket.rs:212); the raw mark_bytes+body are fed to the hasher before parse (:222-224), so a framing/offset mis-split would corrupt record.body.
  • Propagates via crates/history/src/catchup/buckets.rs:450HistoryError::CatchupFailed("Failed to restore live bucket list: …").

Hypotheses (to be triaged)

  1. Record framing / offset drift in next_record() — a mis-read length prefix would hand from_xdr misaligned bytes and produce "xdr value invalid" at a plausible-looking offset. Offset 4483092 is deep in the largest (level-10, 5.77 M-entry) bucket.
  2. A BucketEntry variant henyey's XDR can't decode on a recent testnet bucket (protocol/type the deserializer rejects). Would be a genuine parity gap.
  3. Recently introduced on main (present on 129190c0, deployed 0ac84d42 predates it) — bisect 0ac84d42..129190c0 over crates/bucket/ and the vendored stellar_xdr pin.

Why this matters beyond CI

restore_from_has_parallel is the same code path used by the operator-only cold-catchup recovery for the current mainnet outage (#3905 / #3702: recovery = wipe + cold catchup, which restores the live bucket list from a HAS). If this deserialization defect also affects mainnet buckets on 129190c0, a binary carrying it could fail the recovery catchup. (Not yet confirmed on mainnet buckets — mainnet cold-catchup last succeeded 2026-07-01 on 0ac84d42.) Flagging so a fix lands before any redeploy off 0ac84d42.

Suggested next steps

  • Pull the failing testnet bucket for checkpoint 4378879 and reproduce offline: parse level-10 curr bucket, locate the record at offset 4483092, and inspect whether framing or the entry body is at fault.
  • Compare next_record() framing against stellar-core's bucket record format for the entry preceding offset 4483092.
  • Add a regression test around disk_bucket.rs record framing on a real testnet bucket fixture.

Distinct from #3741 (checkpoint-publish timeout/cancel) — filing separately per the different subsystem + root cause; Related to #3741.


Filed by monitor-tick (validator is separately down under #3905; this is a CI-surfaced correctness finding on 129190c0).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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