Early rate$2,400 of senior audit time for $500. Early members keep the rate as it climbs.$2,400 of senior audit time for $500See how →
F-2026-0021·state-corruption

Appeal quorum failure loses both the activation deadline and the ruling

Fixedescrowarbitrationdispute-resolution
TL;DR

When an appeal panel fails quorum the case is rewound but its activation deadline is not restored. expireStuckAppeal, the only deadline-bounded exit, then always reverts, leaving the deposit and juror stakes locked for an unbounded period.

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
MEDIUM
02Section · Description

Description

expireStuckAppeal is the documented upper bound on a stuck appeal, and hangs on one sentinel:

solidity
if (c.phase != Phase.Appealed) revert WrongPhase();
if (c.appealActivationDeadline == 0) revert WrongPhase();

_activateAppealPanel deliberately zeroes that sentinel once a panel seats (#L1137), to stop a stale deadline letting the winning party pre-empt retryStuckCase. The appeal quorum-failure branch then rewinds the case to Appealed, resetting jurors, ruling, share, every deadline and appealRound (#L770-L780) — but never restoring appealActivationDeadline. The case is now exactly what expireStuckAppeal exists to rescue, and the next line rejects it. This is permanent: the only non-zero writer is fileAppeal (#L905), which requires phase == Tallied.

The appellant's deposit and the round-zero panel's activeAssignments are then held with no deadline and no owner function able to release either. Recovery depends on a fresh eligible panel being drawn, and the locked round-zero jurors are removed from the eligible set in the meantime.

The same branch also sets c.ruling to Resolution.None and c.buyerShareBps to 0, discarding the round-zero outcome although it remains preserved in appealRound1Ruling / appealRound1BuyerShareBps. That is unreachable today precisely because the missing deadline blocks expireStuckAppeal — but execute dispatches with no validity check, so None falls through to resolveDisputeByArbitratorWithSplit(0), paying 100% to the seller. A fix that restores the deadline without also restoring the ruling converts this lock into a mis-payment. The two must be corrected together.

03Section · Impact

Impact

The only deadline-bounded exit from a stuck appeal is destroyed, leaving the deposit and three juror stakes locked for an unbounded period. retryStuckCase survives, so this is loss of the bounded exit rather than a permanent freeze — which is why it is Medium and not High. If the deadline is restored without also restoring the ruling, a case whose appeal merely failed to reach quorum settles the entire disputed principal to the seller, silently inverting the adjudicated outcome.

04Section · Recommendation

Recommendation

One patch to the appeal quorum-fail branch, applied as a single change:

diff
if (isAppeal) {
delete c.jurors;
c.phase = Phase.Appealed;
- c.ruling = Resolution.None;
- c.buyerShareBps = 0;
+ c.ruling = appealRound1Ruling[caseId];
+ c.buyerShareBps = appealRound1BuyerShareBps[caseId];
...
c.appealRound = 0;
+ c.appealActivationDeadline =
+ block.timestamp + ProtocolTimings.APPEAL_ACTIVATION_WINDOW;
} else {

Size the re-armed window at no less than vrfRetryDelay() plus one VRF round trip, so a good-faith retryStuckCase always wins the race. Stamp it from block.timestamp, not the original fileAppeal time, so the deadline is never stale. Add a defensive if (c.ruling == Resolution.None) revert in execute so no present or future path reaches the 0/100 fallthrough. Emit AppealRequeued(caseId, newActivationDeadline) — today a case moves from bounded to unbounded with only QuorumFailed to show for it.

Deleting #L1137 is not the fix — it reintroduces the stale-deadline problem that line solves.

05Section · Resolution

Resolution

Fixed. Appeal quorum failure now preserves the activation deadline, so the bounded exit survives.

06Section · Affected files

Affected files

  • ArbitrationPool.sol#L770-L780, #L905, #L927-L931, #L943, #L972-L980, #L1137
Status
Fixed
F-2026-0021