Skip to content

MAX_ENQUEUED_REMOVALS insufficient for gas limit errors. Datasets stuck #283

Closed

Description

@ZenGround0

We've observed an instance where the gas limit is being hit upon processing nextProvingPeriod. From @TippyFlitsUK:

Short version: while removing the duplicate pieces from Covalent's Storacha migration data set (1245), the scheduled removals made the next nextProvingPeriod tx too big to fit in a block. Gas estimation reverts, so nothing ever hits the chain... proving just silently stops. Every removal call was inside the allowed limits (500 per call, ~2k per proving period) - the caps happily let you build a state the chain can't actually execute.

This has always been my biggest outstanding worry about pdp. The guess at a safe value for the constant was wrong and so we are now observing a liveness issue. Moreover any guess for this constant is going to be wrong since PDPVerifier has no control over the listener contract which may happily eat up a large part of the message/block gas limit.

Here are some solution ideas

external processRemovals call processes the queue before nextProvingPeriod

One way to handle this is to allow partial queue draining via a separate call processRemovals which processes a user specified number of queue entries. When nextProvingPeriod fails with out of gas then callers like curio can move over to using this resumable queue draining call.

This maintains the invariant that all pieces added to the queue will eventually be processed while enusring livenss of nextProvingPeriod under the assumption that the listener call maintains liveness.

A listener call must be added for removalsProcessed so that it can handle tracking of removed pieces.

This new call would need to be handled with care to avoid grinding. It must be enforced that the proving set is not modified between challenge epoch sampling and proving. To accomplish this we will need a compliant listener that asserts that either proving has been missed or proving has completed this period before processing these calls. This is readily achievable with existing state in the filecoin-services contracts.

external cancelScheduledRemovals call empties queue before nextProvingPeriod

The other way to handle this is to simply cancel the queued removals to make space for nextProvingPeriod.

This removes the invariant that all pieces added to the queue are deleted before nextProvingPeriod which means grinding needs to be considered. Considering this further this operation is always safe. All manipulations on the proving set must occur between start of the current proving period and the call to nextProvingPeriod and operate only on the enqueued removals without impacting any proving state between challenge sampling and proving.

Note we'll still need a listener call scheduledRemovalsCancelled to help the listener revert any tracking that occured in the scheduling listener callback.

For this reason we could make cancleScheduledRemovals take in a count parameter or a list of piece ids and things would still be safe. But for simplicity I recommend just doing a single drop operation as an escape hatch guaranteeing liveness.

Recommendation

Cancellation is overall simpler for verifier and listener and reflects the fact that this is an escape hatch not a common case path. I also suspect it will be cleaner in curio.

We should also consider doing measurements on filecoin-services listener listener gas costs and PDPVerifier nextProvingPeriod gas costs and reduce the constant to something that better reflects a safe threshold in today's environment.

Activity

  1. moved this to 📌 Triage in FOCon Jul 2, 2026
  2. snadrus commented on Jul 2, 2026

    @snadrus

    The always-safe option sounds best to me
    So Curio could send 1 remove when it gets the notice that Bulk Removes are cancelled, to verify. Then apply some edge-finding algorithm to find the smaller bulk level that'll work.

  3. rvagg commented on Jul 3, 2026

    @rvagg
    Contributor

    Mostly this should be in the SP's hands to solve to keep things functioning. But, they do get paid for removals, so there should be ample remuneration for handling the hassle. One thing we should be aware of when making this flexible is that the removal fee is queued when schedulePieceDeletions is run, not nextProvingPeriod. So if we're going to allow the SP to de-schedule removals then we need to either move that fee or have a reverse rail or some other kind of debt .. we probably shouldn't move the fee into nextProvingPeriod, this issue shows why we try to keep things out of there.

    I like a separate processRemovals(n) call prior to nextProvingPeriod if we can make it work with the proving system. That would solve the whole thing, if we shunt removals entirely to a separate call (make nextProvingPeriod block if there are removals pending) then we could even properly gas-account for the cost of removals and build that into any pricing update in the future, that would be neat, but would mean mandatory extra calls; so .. tradeoffs.

    Another possibility might be a nextProvingPeriod(n) variant where the SP could choose to process some number of removals less than the total amount queued. But we'd need to consider how to hold the SP accountable for actually making the removals happen over time and not just collecting the removal fee, making a vague promise to the user, but letting pieces sit for many days, or even the entire term of the data set. The differential between "I want to delete these pieces" and when/if they are deleted is already annoying with the 2000 limitation; at least it's clear though, "no more than 2000 per period" is at least a coherent rule, if confusing for users.

  4. moved this from 📌 Triage to ⌨️ In Progress in FOCon Jul 3, 2026
  5. added this to the M4.5: GA Fast Follows milestone on Jul 3, 2026
  6. Kubuxu commented on Jul 13, 2026

    @Kubuxu
    Collaborator

    Another possibility might be a nextProvingPeriod(n) variant where the SP could choose to process some number of removals

    I was going to suggest something similar, but with different semantics. As in the SP has to drain the removal queue to get the proving period assigned. The nextProvingPeriod would have to return a flag meaning "call me again".

  7. ZenGround0 commented on Jul 13, 2026

    @ZenGround0
    ContributorAuthor

    I like a separate processRemovals(n) call prior to nextProvingPeriod if we can make it work with the proving system. That would solve the whole thing, if we shunt removals entirely to a separate call (make nextProvingPeriod block if there are removals pending) then we could even properly gas-account for the cost of removals and build that into any pricing update in the future, that would be neat, but would mean mandatory extra calls; so .. tradeoffs.

    This does seem pretty clean. I'm leaning in this direction.

  8. TippyFlitsUK commented on Jul 22, 2026

    @TippyFlitsUK

    Second stuck data set: 1187 (mainnet, 976 pieces, ~35 GiB) with 386 queued removals. Small set, so not gas. eth_call on nextProvingPeriod:

    event::emit_event -- total event value lengths exceeded the max size: 12480 > 8192
    

    The PiecesRemoved event blows the FVM event cap, so the real threshold is ~250 removals per flush regardless of gas or set size. Same silent presentation as 1245; curio walked it to 4 of 5 failures before we caught it.

    Happy to test against 1187 when there's something deployable.

  9. rvagg commented on Jul 23, 2026

    @rvagg
    Contributor

    filecoin-project/curio#1372; I'm not sure whether the original curio design intended to cap at 200 total or 200 on-chain plus 200 incoming, maybe it should have been 200 total in which case the errant 386 shouldn't have got through in the first place.

  10. Kubuxu commented on Jul 23, 2026

    @Kubuxu
    Collaborator

    How do we end up with 12000 events for 400 removals? That is a 30x multiplier.

  11. rvagg commented on Jul 24, 2026

    @rvagg
    Contributor

    12480 > 8192 is bytes, not events.

    emit PiecesRemoved(setId, removalsToProcess);   // event PiecesRemoved(uint256 indexed setId, uint256[] pieceIds)
    

    386 uint256's = 12,352 bytes plus overhead. So we're hitting a single event ceiling here that's just above 250 pieces.

    On top of this there's also some fixed size events that can be hit in the same tx: DataSetEmpty, NextProvingPeriod, then in FWSS FaultRecord if there were faults, then there's even a bunch of FP events on top of that: AccountLockupSettled / RailRateModified / RailLockupModified / RailOneTimePaymentProcessed.

    ~easy initial fix is to batch PiecesRemoved to something like 100. That lifts that particular limit.

  12. rvagg commented on Jul 24, 2026

    @rvagg
    Contributor

    argh, it's actually even worse. Current foc-observer numbers show a 132M gas per removal; that makes for a hard cap of ~70 removals per nextProvingPeriod, not 250, not even the 200 Curio is encoding. Unfortunately our state growth is hurting us by growing this gas cost of ~5% per month so we'll be down to 50 removals per nextPP in a year if we don't deal with gas.

    We'd better fix this fairly urgently. Even if you did 70 removals for a nextPP then you're spending over 9B gas to do it.

  13. TippyFlitsUK commented on Aug 7, 2026

    @TippyFlitsUK

    Quick update from the operator side after upgrading our mainnet SP to Curio v1.28.3.

    Data set 1187 is still wedged as described above: 386 scheduled removals, nextProvingPeriod reverting with total event value lengths exceeded the max size: 12480 > 8192, last successful proof 2 July. Five-plus weeks unproven now.

    One thing has changed that raises the urgency for other SPs. Curio v1.28.3 removed the per-dataset proving backoff, so this failure is no longer swallowed silently - the task now fails and logs the full revert every couple of minutes. Any SP carrying a wedged set will start seeing it loudly the moment they upgrade to v1.28.3. The invisibility that let this sit unnoticed for weeks on our node is gone, which is good, but it also means more operators are about to find sets in this state.

  14. nalacateria commented on Aug 13, 2026

    @nalacateria

    @ZenGround0 @LexLuthr - can you give a status update on this? This is affecting production SPs with significant error messages.

  15. ZenGround0 commented on Aug 17, 2026

    @ZenGround0
    ContributorAuthor

    Status update:

    1. Tippy's prediction above is likely why we are just now seeing operators concerned about this issue :

    One thing has changed that raises the urgency for other SPs. Curio v1.28.3 removed the per-dataset proving backoff, so this failure is no longer swallowed silently - the task now fails and logs the full revert every couple of minutes. Any SP carrying a wedged set will start seeing it loudly the moment they upgrade to v1.28.3. The invisibility that let this sit unnoticed for weeks on our node is gone, which is good, but it also means more operators are about to find sets in this state.

    The bright side is that we are handling these datasets more correctly now -- they are not getting silently deleted but are instead loudly complaining to the SP that they are in a bad state and need remediation.

    1. We missed that our curio bandaid fix was not enough to patch this up in the short term. A PR further bounding the deletion queue is up here: Make enqueued removals still more conservative filecoin-project/curio#1439.
    2. To understand how many of the errors are persistent logging of existing issues vs new problem delete messages that are creating many new problems it would be very helpful to get reports from SPs on what exactly they are seeing. @TippyFlitsUK @beck-8 do you have any information related to this from yourselves or other SPs?
    3. @LexLuthr has a PDP protocol PR in the works here: Add resumable scheduled piece deletion #297 for resolving the issue for all new and existing datasets. There is ongoing design discussion but imo this can be finished and merged by eow perhaps earlier. Once we get out a PDP contract upgrade problem datasets will resolve themselves as theyll be able to clear out their deletion queues in multiple messages and not hit the gas limit.
    4. We'll need to also finish this Easy PDP work item: PDP: integrate new processRemovals function into pdp tasks filecoin-project/curio#1422 to integrate the new pdp protocol change into curio and fix these datasets. @LexLuthr @snadrus lets plan to get this ready for the pdp upgrade this week as well.
  16. ZenGround0 commented on Aug 17, 2026

    @ZenGround0
    ContributorAuthor

    @snadrus and I did a search through better stack and I'm actually only seeing this issue present itself on Tippy's dataset 1187. For this reason we are not planning to do any emergency curio releases with #1439 and will focus our efforts on getting the pdp protocol update fixing the root cause issue all the way through the stack

  17. TippyFlitsUK commented on Aug 18, 2026

    @TippyFlitsUK

    Hey, Zen 👋

    Careful with the Better Stack read - it only covers SPs shipping logs there, and pre-v1.28.3 nodes swallow this revert silently, so a quiet search doesn't mean quiet chain.

    I swept getScheduledRemovals across all 796 live mainnet data sets this morning: 48 carry a queue, 24 are above the ~70 per flush gas ceiling, 15 above the ~250 event cap. Set 245 is sitting on a full 2,000. Those sets can never resume proving as things stand.

    Also worth knowing 1187 disappears as a sample soon - I terminated it on 10 Aug and it deletes after epoch 6,355,075 (~9 Sep).

    Many thanks, buddy 🙏

  18. ZenGround0 commented on Aug 18, 2026

    @ZenGround0
    ContributorAuthor

    In that case can you please link me to reports of SPs dealing with this problem? We are already working to fix this at the root. If this is causing SPs enough trouble to report an issue then we can consider shipping an emergency release.

  19. TippyFlitsUK commented on Aug 18, 2026

    @TippyFlitsUK

    I'm fully aligned with your root-fix-focus suggestion above. I don't think we need to worry about an emergency release here.

    The datasets I turned up are on nodes that went dark back in March and April, and 1187 is my own and already on its way out. 🚀

  20. moved this from ⌨️ In Progress to ⌚️ Issue awaiting PR merge in FOCon Aug 19, 2026
  21. moved this from ⌚️ Issue awaiting PR merge to 🎉 Done in FOCon Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

  • Status
    🎉 Done
  • Status
    🎉 Done

Relationships

None yet

Development

No branches or pull requests

Issue actions