Description
When a block's transparent input spends an output from a very recent block that is not yet committed to the state, the verifier's AwaitUtxo lookup can time out. Since #10810 the timeout is correctly classified as TransactionError::TransparentInputNotFound (in the AwaitUtxo arm of zebra-consensus/src/transaction.rs) instead of InternalDowncastError β but the syncer then treats that transient condition as a consensus-invalid block.
should_restart_sync() (zebrad/src/components/sync.rs) has no arm for a verify-side transient NotFound: the error falls into the catch-all and triggers a full sync restart. Under a sustained near-tip stall, each restart re-downloads the same range, hits the same not-yet-committed parent, times out again, and restarts again β a restart loop that keeps the node pinned just below the tip.
The catch-all's own diagnostic confirms this error class is reaching a place it was never meant to: because the debug string contains NotFound, it logs "a BlockDownloadVerifyError that should have been filtered out was detected, which possibly indicates a programming error" (see the existing TODOs referencing #2908 and #2909 at that site).
Observed
All 4 cached-state GCP jobs (rpc-submit-block, lwd-rpc-send-tx, lwd-grpc-wallet, lwd-sync-update) timed out together on the 2026-07-31 main run:
https://github.com/ZcashFoundation/zebra/actions/runs/30641561945
Log signature: AwaitUtxo timeout β TransparentInputNotFound β "error downloading and verifying block" WARN β sync restart, repeating.
Verified unfixed on main at 392e4b5 (2026-08-01): no TransparentInputNotFound arm in should_restart_sync(), and no open PR touches the classification.
Possible fixes
Related
Description
When a block's transparent input spends an output from a very recent block that is not yet committed to the state, the verifier's
AwaitUtxolookup can time out. Since #10810 the timeout is correctly classified asTransactionError::TransparentInputNotFound(in theAwaitUtxoarm ofzebra-consensus/src/transaction.rs) instead ofInternalDowncastErrorβ but the syncer then treats that transient condition as a consensus-invalid block.should_restart_sync()(zebrad/src/components/sync.rs) has no arm for a verify-side transientNotFound: the error falls into the catch-all and triggers a full sync restart. Under a sustained near-tip stall, each restart re-downloads the same range, hits the same not-yet-committed parent, times out again, and restarts again β a restart loop that keeps the node pinned just below the tip.The catch-all's own diagnostic confirms this error class is reaching a place it was never meant to: because the debug string contains
NotFound, it logs "a BlockDownloadVerifyError that should have been filtered out was detected, which possibly indicates a programming error" (see the existing TODOs referencing #2908 and #2909 at that site).Observed
All 4 cached-state GCP jobs (
rpc-submit-block,lwd-rpc-send-tx,lwd-grpc-wallet,lwd-sync-update) timed out together on the 2026-07-31 main run:https://github.com/ZcashFoundation/zebra/actions/runs/30641561945
Log signature:
AwaitUtxotimeout βTransparentInputNotFoundβ "error downloading and verifying block" WARN β sync restart, repeating.Verified unfixed on
mainat 392e4b5 (2026-08-01): noTransparentInputNotFoundarm inshould_restart_sync(), and no open PR touches the classification.Possible fixes
should_restart_sync()arm treatingInvalid { TransparentInputNotFound }as transient: drop the block and continue (the syncer will re-request it), restarting only after N consecutive occurrences β mirroring the existing download-sideNotFoundarm. NoteTransparentInputNotFoundis ambiguous (transient timeout vs. genuinely spending a nonexistent output), so it should not be treated as unconditionally transient.Related
lightwalletdtests with regtestΒ #9941 β migrating the affected CI jobs to regtest removes the CI exposure, but this behavior affects any production node near tip and needs fixing regardless.