
Description
Replay a committed on-chain Aptos transaction locally to debug its outcome. Use when investigating a failed or unexpected transaction, reproducing an abort, or testing a local Move patch against a historical transaction.
SKILL.md
When to Use This Skill
Use this skill whenever the user wants to:
- Understand why an on-chain transaction succeeded or failed (Move abort, execution failure, out-of-gas).
- Reproduce a transaction's behavior locally without re-submitting it.
- Test whether a local Move package fix would change the outcome of a committed transaction (regression check for a proposed patch).
- Inspect the storage reads a single transaction issued through the debugger.
The underlying tool is read-only: it fetches the committed transaction and aux info from the network, then executes it against the historical state. It does not mutate any on-chain state.
Tool
Use the move_replay_transaction MCP tool. Do not invoke the Aptos CLI's aptos move replay directly — this tool wraps it and returns structured JSON.
Required Parameters
txn_id(u64) — Committed ledger version of the transaction to replay.network(string) — One of"mainnet","testnet","devnet", or a full REST endpoint URL (e.g."https://my-node.example.com/v1").
Optional Parameters
local_package_paths(string[], default[]) — Paths to local Move packages whose modules override the on-chain versions during replay. Each path must point to a directory containingMove.toml. Use this to simulate a fix.named_addresses(object, default{}) — Named-address bindings ({"name": "0xADDR"}) used when compiling the local packages. Only consulted whenlocal_package_pathsis non-empty.node_api_key(string) — Bearer token sent asAuthorization: Bearer <key>to the node. Use this when the public endpoint is rate-limited.trace(bool, defaultfalse) — Whentrue, record a structured trace of debugger state-view requests (onestate_view { version, with_overrides }entry per call) into the response. Off by default; tracing adds overhead. Only state-view requests are intercepted — the wrapper does not introspect Move bytecode execution itself.trace_storage_reads(bool, defaultfalse) — Whentrue, additionally record onestorage_readentry per state-view read. Off by default because a single replay typically issues hundreds of reads, which crowd out the higher-signal events. Only consulted whentraceistrue.max_trace_events(usize, default500) — Trace truncation limit. Only consulted whentraceistrue. Must be between1and the server-side cap of100_000(inclusive); requests outside that range fail fast with aninvalid_paramserror. Raise it only iftruncated > 0in the response.redact_storage_keys(bool, defaulttrue) — Whentrue, storage-read trace entries omit theDebug-formattedStateKey. Only consulted when bothtraceandtrace_storage_readsaretrue. Disable only when the key contents themselves are needed for debugging.
Constraints
- Only user transactions are supported. Genesis, BlockMetadata, BlockEpilogue, StateCheckpoint, and ValidatorTransaction variants are rejected with a structured
invalid_paramserror. - The tool enforces a server-side timeout. If replay times out, suggest turning
trace_storage_readsback off, dropping local overrides, or raising the server's--tool-timeout.
Interpreting the Response
The tool returns a JSON object with these fields:
| Field | Meaning |
|---|---|
success | true = Keep(Success). false = Keep(<any failure>). null = Discard or Retry (transaction was not committed in the normal sense). |
vm_status | Human-readable VM status, same formatting as the Aptos CLI's replay command. |
abort | Present only when the status is MoveAbort. Includes location ("0xADDR::module_name" or "script"), code, and optional reason / description if the module shipped abort metadata. |
execution_failure | Present only when the status is ExecutionFailure. Includes location, function index, and code_offset within that function. |
transaction_hash | Hex hash of the signed transaction. |
version | Echo of the input txn_id. |
sender | Sender address as a 0x… hex literal. |
sequence_number | Present when the transaction uses sequence-number replay protection; absent for orderless (nonce-based) transactions. |
gas_used, gas_unit_price | Same as on-chain. |
local_override_in_use | true iff local_package_paths was non-empty — i.e. the replay diverged from on-chain bytecode. |
trace | Captured trace entries, only when trace: true was set on the request. Each entry is one of state_view (always exactly one per replay) or storage_read (zero by default; many when trace_storage_reads: true). |
Reading the Status
success == true→ transaction would commit normally. If the user expected a failure, double-check the inputs.success == falsewithabortpopulated → a Moveabortwas hit. Report:abort.location(which module),abort.code(raw code),abort.reason/abort.descriptionif available (these come from#[error]/ abort-info metadata in the module),- The matching constant in the source if the reason name is symbolic (e.g.
EINSUFFICIENT_BALANCE).
success == falsewithexecution_failurepopulated → a non-abort runtime failure (arithmetic overflow, type error, vector bounds, etc.). Reportlocation,function, andcode_offset; offer to disassemble the module if the user wants the exact bytecode site.success == falsewith neither populated → likelyOutOfGasorMiscellaneousError. Thevm_statusstring carries the detail.success == null→ transaction wasDiscarded or markedRetry. Thevm_statusstring explains why; common causes are signature/validation issues that prevent execution.
Workflows
A. Plain Debugging — "Why did this transaction fail?"
- Confirm with the user which
networkthe transaction lives on. - Call
move_replay_transactionwith justtxn_idandnetwork. - Read
successfirst; then drill intoabortorexecution_failure. - If the user wants the source-level reason, query the module with
move_package_query(or read the module source) to find the constant matchingabort.code/ the function atexecution_failure.function.
B. Patch Testing — "Would my fix change this transaction's outcome?"
- Ask for (or locate) the local Move package that re-implements the relevant module(s). It must be a buildable package with a
Move.toml. - Determine the named-address bindings required to compile it. They must resolve every named address used in the package's source.
- Call the tool with:
local_package_pathsset to the package directory (or list of directories),named_addressesmapping each name to its on-chain address,- the same
txn_idandnetworkas the failing transaction.
- The response will have
local_override_in_use: true. Compare itssuccess/abort/execution_failureagainst the unmodified replay (workflow A) to see whether the patch changed behavior. - Important: if the patched module's bytecode is type-incompatible with the on-chain version (different public function signatures, removed structs, etc.), the VM will fail at link time — surface this clearly rather than treating it as a Move bug.
C. Tracing — "Show me what the VM did step by step"
- Start with
trace: truealone. You will get exactly onestate_view { version, with_overrides }entry — the state view the VM consumed for the run. Withwith_overrides: falseyou can confirm the on-chain path was taken; withwith_overrides: trueyou can confirm the local-override path was taken. For most "why did this fail" questions this entry plus the structuredabort/execution_failurefields are all you need. - Only set
trace_storage_reads: truewhen you specifically need to see whichStateKeys were touched during execution. Expect hundreds of entries per replay; raisemax_trace_events(e.g. to5000) when you do this. Leaveredact_storage_keys: trueunless you need theDebug-formatted key bytes. - When reporting back, enumerate the actual entries verbatim — never collapse them to counts. The single
state_viewentry should always appear in the output you show the user; quote thestorage_readentries one by one when the user asked for them. - If the response shows
truncated > 0, the cap was hit. Either raisemax_trace_eventsor turntrace_storage_readsback off. - The trace is at debugger-wrapper granularity — it does not introspect Move bytecode execution itself, so it cannot show the in-Move call frame that hit an abort. Use the structured
abort/execution_failurefields plus a module query for that.
Reporting Results
When summarizing for the user:
- Always quote
success,vm_status, and the structuredabort/execution_failurefields verbatim — these are the ground truth. - When citing an abort, give both the symbolic reason (if present) and the raw code; the symbolic name can be absent for older modules.
- If
local_override_in_use == true, label the result as "replayed with local overrides" so the user does not confuse it with the on-chain outcome. - Do not speculate about state changes beyond what the tool returned. If the user wants deeper post-state inspection, suggest re-running with tracing enabled rather than guessing.
More skills from the aptos-ai repository
View all 7 skillsmove
develop applications on Aptos
Jul 19BlockchainWeb3move-check
check Move packages for compilation errors
Jul 19DebuggingWeb3move-inf
infer specifications for Move packages
Jul 19Data ModelingWeb3move-init
initialize Move workflow routing
Jul 19ConfigurationWeb3move-prove
verify Move specifications with Move Prover
Jul 19TestingWeb3move-test
generate unit tests for Move code
Jul 19TestingWeb3
More from Aptos Labs
View publisheranalyze-gas-optimization
optimize Aptos Move contracts for gas
aptos-agent-skills
Jul 12BlockchainPerformanceSmart Contractscreate-aptos-project
scaffold new Aptos dApp projects
aptos-agent-skills
Jul 12Next.jsTypeScriptViteWeb3deploy-contracts
deploy Move contracts to Aptos networks
aptos-agent-skills
Jul 12DeploymentEngineeringSmart Contractsgenerate-tests
generate test suites for Move contracts
aptos-agent-skills
Jul 12EngineeringQATestingmodernize-move
modernize Move V1 contracts to V2
aptos-agent-skills
Jul 12Code AnalysisEngineeringMigrationsearch-aptos-examples
search Aptos reference implementations
aptos-agent-skills
Jul 12BlockchainDocumentationSearch