Quick Reference
| Error | Likely Cause | Quick Fix |
|---|---|---|
| ”Expected 1 assertion to be executed, but 0 were executed” | Transaction reverts or setup after cl.assertion() | Check trigger function, move setup before cl.assertion() |
| OutOfGas error | Assertion exceeds 300k gas limit | Optimize loops, add early returns, fail fast |
vm.load() not available | Wrong cheatcode | Use ph.load() instead |
| Function selector errors | Wrong selector or trigger not registered | Use Protocol.functionName.selector, check triggers() |
| Call input double-counting | Using getAllCallInputs() | Use specific call type function |
| Test contract address in error | Inline function call consumed vm.prank() | Store view function results before vm.prank() |
| Cheatcode fails in constructor | Cheatcodes not available in constructors | Move logic to assertion function |
Assertion Not Executing
”Expected 1 assertion to be executed, but 0 were executed”
This error means your assertion never ran on the target transaction. This can occur in production, testing, or backtesting. Common Causes: In production or backtesting:- The transaction that triggers the assertion fails or reverts before the assertion runs
- The function selector in the trigger doesn’t match the function being called
- Wrong function called - A call to a function other than the target function exists between
cl.assertion()and the target function call - Assertion (
cl.assertion()) placed incorrectly - Not immediately before the target function call - Trigger function call fails or reverts - The transaction may fail for various reasons. Write a test without
cl.assertion()to isolate the issue.
- If an assertion reverts, the transaction is dropped and not included in the block
- If the assertion never executes (trigger doesn’t match), the transaction proceeds normally without assertion validation
cl.assertion(). The cl.assertion() call registers the assertion to run on the very next transaction, similar to vm.prank().
- Verify the function selector in the trigger matches the function being called
- In testing, write a test without
cl.assertion()to check if the protocol function reverts - Use
pcl test -vvvto see detailed execution traces
Internal Calls Not Triggering Assertions
Problem: Assertion doesn’t trigger when a protocol uses internal function calls. Cause: Internal calls are a Solidity language feature, not actual EVM calls. They are not traced and won’t trigger call-based assertions. Example:this.functionName()) generate CallInputs and can trigger assertions.
Assertion Code Issues
These errors occur when the assertion executes but fails due to issues in the assertion code.Gas Limit Exceeded
Assertions have a 300k gas limit per assertion function. If exceeded, you’ll see anOutOfGas error.
Symptoms:
OutOfGaserror when running tests- “Unknown Revert Reason” with gas cost near 300k in backtesting results
- Assertion fails consistently on complex transactions
- Expensive operations in loops
- Parsing too many events
- Multiple storage reads without caching
- Complex calculations without early returns
- Fail fast - Check simple conditions first before expensive operations
- Optimize loops - Limit iterations, cache values outside loops
- Share storage reads - Read once, extract multiple values
- Split complex assertions - Consider splitting into multiple assertion functions
pcl test -vvv to see detailed gas usage per operation.
Storage Access Errors
Error:vm.load() is not available in assertions
Solution: Use ph.load() instead:
ph provides storage access, not the standard Forge vm cheatcode.
Constructor Limitations
Problem: Cheatcodes fail or return unexpected values in assertion constructors. Causes:- Cheatcodes (
ph.*) are not available in constructors - Constructor runs against empty state and cannot read from other contracts
ph.getAssertionAdopter()is only available in assertion functions
Function Selector Errors
Problem: Wrong function selector in trigger registration causes the assertion to not trigger or trigger on the wrong function. Solution: Use the actual function selector from the interface:- Check that the selector matches the function signature
- Use
Protocol.functionName.selectorfor type safety - Ensure the trigger is registered in the
triggers()function - Each assertion function needs its own trigger registration
Call Input Double-Counting
Problem: UsinggetAllCallInputs() may include duplicate calls from proxy contracts.
Solution: Use specific call type functions:
ph.getCallInputs()- CALL opcode inputsph.getStaticCallInputs()- STATICCALL inputsph.getDelegateCallInputs()- DELEGATECALL inputsph.getCallCodeInputs()- CALLCODE inputsph.getAllCallInputs()- All call types (may include duplicates from proxy contracts)
Test-Specific Issues
These issues only occur during testing, not in production.Inline Function Calls Consuming vm.prank()
When you pass a function call as a parameter, that inner call executes first and consumes the vm.prank().
Problem:
vm.prank():
- Solidity evaluates function parameters before executing the outer function
vm.prank()only affects the next external call- The inner function call is that “next call”
- By the time the outer function executes, the prank is already consumed
-vvv to see call traces. Look for unexpected addresses in error messages (test contract address where user expected).
Can’t Distinguish Protocol vs Assertion Revert
Problem: When a test reverts, it’s not clear whether the protocol function or the assertion caused it. Solution: Write a test withoutcl.assertion() to isolate the issue:
pcl Command Issues
Build and Test Failures
Common Causes:- Missing
FOUNDRY_PROFILE=assertionsenvironment variable (see Foundry Profile Configuration) - Incorrect
foundry.tomlconfiguration - Missing dependencies or remappings
FOUNDRY_PROFILE=assertions before running pcl commands:
- Check
foundry.tomlhas assertions profile configured - Verify
remappings.txtincludescredible-std/=lib/credible-std/src/
Backtesting Errors
For backtesting-specific errors, see the Backtesting Guide. Common Backtesting Issues:- ASSERTION_FAIL - Assertion reverted (may be false positive, gas limit exceeded, or known exploit)
- UNKNOWN_ERROR - Unexpected failure (check error message, may be RPC issue)
- NEEDS_REVIEW - Transaction called a function not monitored by your assertion, or failed to replay
Debugging
Using Verbose Output
Usepcl test -vvv to get detailed information:
- Call traces - See which functions executed and in what order
- Gas usage - Identify expensive operations causing gas limit issues
- Error messages - Get detailed revert reasons and stack traces
- State changes - Track storage modifications
- Unexpected function calls between
cl.assertion()and target function - Gas consumption hitting the 300k limit
- Wrong addresses in call traces (indicates
vm.prank()issues) - Revert locations to identify assertion vs protocol failures
Console Logging
Addconsole.log() statements to your assertion functions for debugging. Note that console.log() only accepts a single string argument, so use string concatenation for values:
pcl test, not in production.
Getting Help
If you’ve tried the solutions above and still can’t resolve your issue:- Check the error message - Look for specific error details in verbose output
- Isolate the problem - Test protocol function without assertion to identify root cause
- Gather information:
- Error message and stack trace
- Test code that reproduces the issue
- Gas usage (if gas-related)
- Verbose output (
-vvv)
- GitHub Issues - Report bugs or ask questions
- Telegram - Community support
- Writing Assertions Guide - Learn assertion patterns
- Testing Assertions - Testing best practices
- pcl Reference - Command-line tool documentation
- Cheatcodes Reference - Available Phylax cheatcodes

