# Teku incorrectly retrieves pending partial withdrawals, leading to chain split due to incor- rect "expected withdrawals"

Teku's getPendingPartialWithdrawals bounded the loop by iteration count instead of produced withdrawals, yielding wrong expected withdrawals and potential chain split.

- Payout: Contest reward
- Severity: medium
- Program: Ethereum Pectra Teku
- Credited: zigtur
- Disclosed: 2025-07-21
- Write-up quality: 7/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_ethereum_pectra_teku.pdf#3.1.1
- Library page: https://bugbountylibrary.com/r/cantina%3Aethereum_pectra_teku%3A3.1.1
- Tags: domain:web3 impact:dos impact:integrity kind:contest platform:cantina sev:medium tech:java technique:regression technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Beacon chain consensus client (ExpectedWithdrawals / getPendingPartialWithdrawals)
**Root cause:** Loop condition uses `i < maxPendingPartialWithdrawals` (iterations) instead of `len(partialWithdrawals) < maxPendingPartialWithdrawals` (produced withdrawals); skipped validators (queued for exit, insufficient balance) consume the budget, so fewer withdrawals are emitted than the spec requires.
**Steps:**
1. Read spec get_expected_withdrawals: loop breaks when `len(withdrawals) == MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP`, not on iteration count.
2. Inspect Teku getPendingPartialWithdrawals: `for (int i = 0; i < pendingPartialWithdrawals.size() && i < maxPendingPartialWithdrawals; i++)`.
3. Build Electra preState with 3 pending partial withdrawals where one validator is queued for exit (skipped).
4. Call `spec.getBlockProcessor(slot).getExpectedWithdrawals(preState)`; observe withdrawalList size 1 instead of 2 and partialWithdrawalCount 2 instead of 3.
5. Compare against spec reference implementation to confirm divergence in expected withdrawals payload.
**How found:** Code-reading against the Electra spec pseudocode: noticed the loop bound semantics differ (iteration count vs. appended-withdrawal count) and that skipped validators don't append, then wrote a unit test with a queued-for-exit validator to demonstrate the mismatch.
**Exploitation:** Not directly weaponized; a Teku node computing divergent expected withdrawals would produce a block that other clients reject, splitting ~27-28% of the network off consensus.
**Insight:** When porting spec pseudocode that bounds a loop by output-list length, check whether the port bounds by input index instead — any branch that skips appending silently shrinks the output and diverges from consensus.
