Skip to content
Runner · Write your pluginStep 5 of 6

When it doesn't work

The first failures and how to read them.

Derived from runner/src/engine/bin/inspect.ts

Your first runs will fail. That's the ramp working. Every early failure has a shape, and one command shows you that shape before you spend a tick on it:

pnpm inspect my-strategy   # wallet, live vault state, granted permissions. No tx, no signing.

inspect loads the plugin, fetches its vault, and prints what decide() would see right now. It answers one question: why isn't my plugin acting? Here are the four answers it gives first.

Missing or wrong key

The plugin loads, but the Wallet section reads:

Wallet: ❌ could not load — ...

inspect reads your operator key from the env var named by operatorPrivateKeyEnv (default OPERATOR_PRIVATE_KEY). If it's unset, malformed, or not a suiprivkey1... bech32 string, the load throws and inspect reports it instead of crashing. Fix the env, re-run, and confirm the printed Address is the one you expect.

This key doesn't operate the vault

The wallet loads and the vault fetches, but the Address inspect prints is not the vault's operator_address. Nothing on screen is red, yet every real tick will revert.

A discretionary vault has exactly one operator_address. The contract checks sender == operator_address before it runs any strategy action; a tx signed by anyone else aborts.

So inspect surfaces this by showing you the address you're actually signing with. Compare it to the operator the vault was deployed with. If they differ, you're pointing at the wrong vault or driving it with the wrong key. The full rule — one operator per vault, no mixing bot and human — is in Runner architecture.

The vault is frozen

The Vault state block prints three flags:

Paused:               false
Strategy paused:      false
Frozen (computed):    false

Frozen (computed) is paused || strategyPaused. When it's true, the contract rejects every strategy action, no matter what decide() returns. Paused is a full governance halt; Strategy paused stops trading while still allowing redemptions. Don't waste a tick guessing — read the two flags and you know which lever is down and who has to lift it.

No room and no signal

Sometimes nothing is broken and the plugin still does nothing. inspect shows you why:

  • Exposure headroom — how much exposure you can still add. At 0, every mint hits the vault's aggregate cap and reverts. The plugin should return noop, not a doomed Action[].
  • Granted strategy permissions — the actions this strategy is allowed to call. If your plugin tries to vault.mintBinary and this list reads (none — strategy can only call permissionless ops), the permission was never granted. Grant it, or the action reverts on-chain.
  • Open positions, Free quote, NAV — the rest of the state your decide() reads. If the market conditions your strategy waits for aren't here, a noop is the correct, healthy outcome.

A plugin that returns noop because there's nothing to do is working. inspect is how you tell that apart from a plugin that's silently misconfigured.

From here, the Build ladder takes you from these read-only checks to a live, multi-trigger agent.