How to Fix Move Prover Timeouts on Large Aptos Modules
Move Prover timeouts on large Aptos modules occur when the formal verification tool exceeds its allocated resources trying to analyze complex specifications across many functions. The fix involves reducing the verification scope, optimizing specifications, and tuning prover parameters - not rewriting your entire module.
Understand why timeouts happen
The Move Prover translates your Move code and its specifications into SMT (Satisfiability Modulo Theories) formulas, then passes them to a solver like Z3. Large modules amplify two problems:
- Specification explosion: Each
ensures,aborts_if, andinvariantadds constraints. When a module has dozens of functions with detailed specs, the solver must satisfy all of them simultaneously across the call graph. - Loop and vector unrolling: The prover unrolls loops and expands vector operations. On modules with nested loops or large vectors, unrolling depth multiplies the formula size.
Timeouts are not a bug in your code. They are a resource-boundary problem.
Step 1: Isolate the Culprit
Before tuning, find which function or spec causes the timeout. Run the prover on individual functions:
move prove --f <function_name>
If a single function proves quickly but the full module times out, the issue is cross-function interaction - likely invariants or postconditions that reference state modified by many functions. If a specific function times out alone, that function's spec is too heavy.
Step 2: reduce specification scope
The most effective fix is to verify less at once. Three strategies:
Split the module into smaller verification units
Create separate Move files (or spec modules) for logically distinct parts of your system. For example, separate admin functions from user-facing ones. Verify each file independently. The prover’s solver works on a smaller model.
Use pragma to limit verification
Add these pragmas to your spec blocks:
spec module {
pragma verify = false; // turn off module-level verification
}
Then selectively enable verification on critical functions:
spec my_critical_function {
pragma verify = true;
// ... specs
}
Remove non-essential specs
Audit your specifications. For each ensures and aborts_if, ask: Does this protect an invariant that must hold for security? Remove specs that describe internal implementation details rather than core correctness properties. A function that only computes a value may not need a full postcondition - just an aborts_if on its error paths.
Step 3: tune prover parameters
The Move Prover accepts configuration flags. Increase timeouts selectively:
move prove --timeout 120
Default is usually 40 seconds. 120 seconds works for many medium modules. For very large modules, try 300 but watch your CI costs.
Other useful flags:
--z3-trace- shows Z3 solver output; helps identify which constraint causes slowness.--cvc5=true- try an alternative solver. CVC5 sometimes outperforms Z3 on certain formula shapes.--no-verify- skip verification entirely for a build; useful to separate compilation from proving.
Step 4: Restructure Loops and Vectors
If a function times out due to loops, consider:
- Add loop invariants - Without invariants, the prover unrolls loops fully. A good loop invariant lets it reason symbolically. Example:
spec my_loop {
invariant i <= len(v);
invariant forall j in 0..i: v[j] > 0;
}
-
Limit unrolling depth - Not standard in Aptos Move Prover, but you can refactor to use
vector::lengthandvector::borrowoperations that the prover handles symbolically rather than iterating. -
Avoid large constant vectors in specs - A spec that constructs a 100-element vector as a literal will force massive unrolling. Compute the expected result differently.
Step 5: use module-level invariants sparingly
invariant statements on module state are powerful but expensive. Each invariant is checked after every function that modifies the state. If you have:
spec module {
invariant my_counter <= MAX;
}
And ten functions modify my_counter, the solver checks this invariant ten times across the call graph. Prefer function-level aborts_if and ensures that constrain the state only at function boundaries.
When to Accept a Timeout
Not every function needs formal verification. For large Aptos modules, a practical approach is:
- Fully verify the core economic or security-critical paths (token transfer, permission changes, oracle updates).
- Leave utility functions (getters, formatting, logging) unverified.
- Verify integration points between sub-modules separately rather than in one monolithic run.
The Move Prover is a static analysis tool - it cannot prove your module is correct if it cannot finish. A partial proof with clear boundaries is better than no proof at all.
Not financial advice. suiboxer.xyz publishes market data and general information about digital assets. Crypto assets are volatile and you can lose everything you put in. Nothing here is a recommendation to buy, sell or hold, and we make no price predictions.
Prices are sourced from third parties and may be delayed or wrong. Verify anything you intend to act on against a primary source.