affaan-m/ECC · error · ValueError
min_amount_out is required before send
Error message
min_amount_out is required before send
What it means
In `safe_execute`, after `self.w3.eth.call(tx)` simulates the transaction, the function requires an `expected_min_out` to enforce slippage. If it is `None`, the function raises `ValueError("min_amount_out is required before send")` and never signs. This is a guard against broadcasting a swap with no slippage protection.
Source
Thrown at skills/llm-trading-agent-security/SKILL.md:82
daily = self._get_24h_spend()
if daily + usd_amount > MAX_DAILY_SPEND_USD:
raise SpendLimitError(f"Daily limit: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_USD}")
self._record_spend(usd_amount)
```
### Simulate before sending
```python
class SlippageError(Exception):
pass
async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:
sim_result = await self.w3.eth.call(tx)
if expected_min_out is None:
raise ValueError("min_amount_out is required before send")
actual_out = decode_uint256(sim_result)
if actual_out < expected_min_out:
raise SlippageError(f"Simulation: {actual_out} < {expected_min_out}")
signed = self.account.sign_transaction(tx)
return await self.w3.eth.send_raw_transaction(signed.raw_transaction)
```
### Circuit breaker
```python
class TradingCircuitBreaker:
MAX_CONSECUTIVE_LOSSES = 3
MAX_HOURLY_LOSS_PCT = 0.05
def check(self, portfolio_value: float) -> None:
if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:View on GitHub (pinned to 01e15490f0)
Solutions
- Compute `expected_min_out` from a fresh quote minus slippage tolerance and pass it explicitly.
- If you genuinely want to skip the check in a sandbox, do not — instead set `expected_min_out=0` and accept that you have no slippage protection.
- Make the parameter required (drop the `= None` default) so the error becomes a `TypeError` at call time, not a runtime `ValueError`.
- Add a unit test that `safe_execute(tx)` without `expected_min_out` raises before any RPC call.
Example fix
# before
async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:
sim_result = await self.w3.eth.call(tx)
if expected_min_out is None:
raise ValueError("min_amount_out is required before send")
# after — required param, compute slippage-bounded minimum at the call site
async def safe_execute(self, tx: dict, expected_min_out: int) -> str:
sim_result = await self.w3.eth.call(tx)
actual_out = decode_uint256(sim_result)
if actual_out < expected_min_out:
raise SlippageError(...) Defensive patterns
Strategy: validation
Validate before calling
def has_min_out(x) -> bool:
return isinstance(x, int) and x >= 0 and x is not None Type guard
null
Try / catch
try:
tx_hash = await agent.safe_execute(tx, expected_min_out=min_out)
except ValueError as e:
if 'min_amount_out' in str(e):
min_out = compute_min_out(quote, slippage_bps)
tx_hash = await agent.safe_execute(tx, expected_min_out=min_out)
else:
raise Prevention
- Make `expected_min_out` a required positional argument so misuse is a TypeError at call time.
- Always compute `amount_out_min` from a fresh quote before constructing the tx.
- Add a unit test that calling `safe_execute` without the minimum raises before any RPC.
When it happens
Trigger: Caller invokes `safe_execute(tx)` positionally, omitting `expected_min_out`. The route/agent computed the call data but never resolved a minimum output (e.g. forgot to call `quote` or set `amount_out_min`).
Common situations: Refactor that added the `expected_min_out` parameter but did not update all call sites. Dev environment passed `None` as a 'skip check' flag that the production code now refuses. Off-by-one in `decode_uint256` upstream left the value unset.
Related errors
- Simulation: {actual_out} < {expected_min_out}
- Single tx ${usd_amount} exceeds max ${MAX_SINGLE_TX_USD}
- Daily limit: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_US
- Invalid mode "${mode}". Allowed modes: ${allowedModes.join('
- Potential prompt injection: {text[:100]}
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/e4efc47078d45a40.
Report an issue: GitHub.