# Batch append past hard cap

Appends to batches, queues or trackers that skip a length-vs-cap check, stalling the pipeline

_Area: Smart contract access control and control flow. Seen in 2 disclosed reports; latest 2026-03-13. Updated 2026-10-23._

## How it works
A container (batch, queue, tracker) is meant to hold at most N items, and later logic assumes that cap holds. The append path never checks the current length, so a write can land on an already-full container. Two triggers recur: a cut/rotation step reuses a key (same block height, same epoch) and appends again, or a retry/replacement mechanism mints new identifiers. Once the container exceeds its cap, any validation that checks `length <= max` always fails, and the dependent pipeline stalls permanently. A related shape: progress gated on a counter of outstanding items that an external party can keep from resolving.

## Where it shows up
- Append sites that push into `map[key]` or a slice with no `len(...) < max` guard.
- Cut/rotation logic that copies a full batch into a key derived from `block.number` or epoch, then appends in the same block.
- Validation (`ensureValidCheckpoint`, upgrade gates) that reverts when a count exceeds a max.
- Trackers with a hard cap on stored IDs (`len(HashList) < 2`) fed by retry/replacement logic that mints new IDs.
- Upgrade/progress gates keyed on an outstanding-item counter (`HasInflightPackets`) where a downstream party controls resolution [E2](https://bugbountylibrary.com/r/h1%3A2914705.md).

## How to review
For every append, state the invariant: container length never exceeds its cap on any path. Check the cut/rotation branch separately from the normal branch, and ask what happens when two cuts occur in the same block or epoch. Confirm the cap is enforced at write time, not only at read/validate time. For trackers, compare the cap against the retry mechanism: can replacements generate more IDs than the cap allows? For progress gates, ask who can prevent an outstanding item from resolving and whether that blocks a required transition forever.

## Variants seen
- Batch cut reuses `block.number` key; committing 21 messages in one block appends past `maxMsgsPerBottomUpBatch`, checkpoint unexecutable [E1](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-04.md).
- Forwarding defers ack-writing to a downstream channel; a malicious channel withholds acks, inflight packets never clear, upgrade never completes [E2](https://bugbountylibrary.com/r/h1%3A2914705.md).
- Outbound tracker caps hashes at two; gas-price bumps create new hashes that are dropped, queue stalls [E3](https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AM-11.md).

## Impact and severity
Permanent halting of checkpoints [E1](https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-04.md), channels left unupgradable forever [E2](https://bugbountylibrary.com/r/h1%3A2914705.md), blocked outbound queues [E3](https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AM-11.md). High severity because the failure is persistent, not transient: no timeout or retry clears it. It worsens when the trigger is cheap (a single block's messages) or attacker-controlled (a malicious downstream channel).

## Why fixes fail
Evidence does not show incomplete or regressed fixes.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-04.md
- E2: https://bugbountylibrary.com/r/h1%3A2914705.md
- E3: https://bugbountylibrary.com/r/c4%3A2023-11-zetachain%3AM-11.md
