Troubleshoot a hook
Locate the failing layer before changing policy or weakening validation.
Identify the failing operation, program, phase, and logs using the callback contract. Start by finding whether failure occurred in core validation, CPI authentication, policy evaluation, reply validation, or settlement.
Diagnose by symptom
| Symptom or error | Likely cause | Fix and verify |
|---|---|---|
UnsupportedHook during setup | Unknown/delta bits, CustomAccounting, or DynamicFee without BeforeSwap | Select supported capabilities and validate the profile |
InvalidHookTarget | Wrong sentinel, nonexecutable program, or core used as its own hook | Correct target binding and program loading |
InvalidHookAccounts | Descriptor/account mismatch, wrong role/address/owner, duplicates, protected alias, signer extra, or missing transaction writability | Rebuild the exact account list and descriptors from authentication |
InvalidHookPermission | Requested phase access or count exceeds pool configuration; invalid external-program declarations | Check per-phase permissions, including both callbacks |
InvalidHookContext | Inconsistent operation details, sequence, snapshots, direction, or settled result | Use the current typed ABI and matching core build; never fabricate callback context |
MissingCoreSignature | Hook was called directly or wrong callback authority supplied | Send the operation through core; retain PDA validation |
InvalidPoolState | Wrong pool version or callback outside entered state | Use the matching core and account contract; do not forge the lifecycle flag |
InvalidPolicyKind | Economic example configured for more than BeforeSwap or wrong profile | Use its exact phase/profile pair |
AlreadyConfigured | Repeated setup against existing policy | Use the existing policy; setup is not an update operation |
PolicyRejected | Closed time, expired/mismatched proof, or fee bounds | Read the structured hook_policy_rejected reason and reproduce the exact boundary |
InvalidPayload | Nonempty fee/window payload or malformed proof/probe payload | Use the policy's documented encoder |
MissingHookResponse | No return data or a different program supplied it | Set the final canonical reply from the configured hook |
InvalidReplyPhase / InvalidFeeResponse | Reply mismatches phase, profile, or required fee presence | Validate HookReply against configured capabilities |
FeeCapExceeded | Dynamic reply exceeds immutable pool cap | Correct the schedule/reply; do not silently clamp unauthorized policy |
Custom error 7000..7009 | Lifecycle probe intentionally rejects phase error - 7000 | Use empty payload for success; keep phase bytes for rollback tests |
Core errors and example-program errors are separate namespaces. Interpret names and logs in the context of the program that failed; the same numeric Anchor offset is not a global error identity. See core errors.
A callback never appears
Confirm its bit is enabled and the operation actually has that callback. Opening/closing a position and collecting fees have none. Before hooks also do not run if account or argument validation fails earlier. A zero liquidity delta rejects rather than becoming a fee-claim callback.
For initialization, do not expect an empty/unassigned pool snapshot. Both initialize callbacks see the serialized candidate pool. A policy that can be configured only after creation is not available during that bootstrap.
A valid proof works for one account but fails for another
Recompute the full domain and authenticated actor. An allowlist leaf is bound to actor, expiry, pool, mint, core protocol, root version, and configured chain namespace. Matching the sibling list alone is insufficient. Use leaf and proof construction to isolate byte encoding from runtime identity.
An after hook fails after token transfers
This still rejects the transaction. Verify committed state and token balances rather than treating an intermediate log as settlement. The payer fee can remain charged. Compare all affected accounts and identify the exact rejecting phase. Preserve authentication and reply checks while correcting the underlying policy or account mismatch.
A working native swap is rejected by Metis
Check that a matching HookQuoteAdapter is bound, all declared policy accounts and a coherent Clock snapshot were refreshed, and required actor/payload context is present. The external evaluator may also be rejecting the policy itself, such as a closed window or expired proof. Continue with Metis compatibility; do not suppress rejection or quote a different policy.