---
title: Replay a message at two blocks
description: Use HexKit's dry run to compare how a Vara program replies to the same message at two blocks, then reproduce the result from the terminal.
url: https://hexdocs.thally.app/guides/replay-a-message
lastUpdated: 2026-10-27
lastVerified: 2026-10-27T00:00:00.000Z
---

# Replay a message at two blocks

Use HexKit's dry run to compare how a Vara program replies to the same message at two blocks, then reproduce the result from the terminal.

import { ReadOnlyNote } from '/snippets/read-only-note.mdx'

A message that worked last week can fail today because the program's state has changed. HexKit's dry run sends the same message to the program as it was at two different blocks and puts the replies side by side, so you can see whether a message would still succeed before anyone pays to send it.

This guide replays the message from the [Quickstart](/quickstart).

## How a dry run works

Each run calls the node's `gear_calculateReplyForHandle` method at a block you pin. The program handles the message against its state at the end of that block and returns the reply it would have sent. Nothing is signed or broadcast.

Messages that ran earlier inside the same block are not replayed first. A dry run can therefore succeed where the original message failed, or the other way round.

## Compare two snapshots

#### Open the message's dry run

    Inspect `36589852-1` in [Message Inspector](https://hexkit.app/vara/transactions?tool=message&id=36589852-1), then open the **Dry run** tab.

#### Check the inputs

    HexKit fills in two snapshots from the original message:

    - **Snapshot A** uses block `36589851`, the block before the message was sent.
    - **Snapshot B** uses `latest`.

    Both use the original sender, value, gas limit and arguments, `{"basket_id": 1633}`. Open **More inputs** on a snapshot to see or change the sender (origin), value, gas limit and raw payload.

#### Run both

    Select **Run both**. Rows whose results are different are marked **differs**.

      <img src="/images/vara-dry-run-compare.png" alt="Dry run results: snapshot A at block 36589851 replies success Auto; snapshot B at the latest block panics with SettlementNotProposed" />

## Read the result

| | Snapshot A (block 36589851) | Snapshot B (latest) |
| --- | --- | --- |
| Reply code | `{"success":"Auto"}` | `{"error":{"execution":"UserspacePanic"}}` |
| Reply | Empty reply | `called Result::unwrap() on an Err value: SettlementNotProposed` |

Before the message, the program accepts it. Today it panics with `SettlementNotProposed`, because there is no proposed settlement left for basket 1633. The `SettlementFinalized` event in the Quickstart shows why: this message finalized it. Sending the same message again would fail and still cost gas.

The block number for `latest` changes every few seconds, so yours will differ from the screenshot. The reply stays the same unless the program's state changes again.

## Try other inputs

- **Another block:** type a block number into a snapshot and select **Run A** or **Run B** to run that snapshot alone.
- **Other arguments:** edit **Args (JSON)**, select **Encode** to rebuild the payload, then run the snapshot again.
- **Send it for real:** **Send transaction** sends the snapshot as a real message. It needs a connected Vara wallet and spends VARA on gas.

## Reproduce from the terminal

Each snapshot has a **vara-wallet** copy button. It gives you the same dry run as a command for Gear's [vara-wallet](https://github.com/gear-foundation/vara-wallet) CLI, which you can install with `npm install -g vara-wallet`.

Run the snapshot before you copy it. The command pins the block by the hash from that snapshot's last run. If the snapshot hasn't run at its current block, the command has no `--at` flag and checks the latest block instead.

Snapshot A from this guide:

```bash
vara-wallet --ws wss://archive-rpc.vara.network message calculate-reply \
  '0xa749ccd80d71637b450789e12e3d94524e9ae17877d1b59f5ddda784f89a2cba' \
  --payload '0x304261736b65744d61726b65744846696e616c697a65536574746c656d656e746106000000000000' \
  --origin '0xb2b013409822741dd78353d8d1f7f68a469c81e42335baeb3d8bf802514e2148' \
  --at '0xa6554d01be62e32f86b4c2e6d524459c2e6ad54d4c24476a7ea7cd3d1c9d785e' \
  --value '0' --units raw
```

It prints the same reply code that HexKit showed for Snapshot A:

```json
{"payload":"0x","value":"0","valueRaw":"0","code":"{\"success\":\"Auto\"}"}
```

Put the command in a script or CI job when you need to check the same message again later.

## Troubleshooting

Chain and program errors appear in the **Reply** row of the result table. Input errors appear in red under the snapshot's buttons.

| Message | What it means | What to do |
| --- | --- | --- |
| `Program not active at block N` | The program did not exist yet at that block, or had exited. | Pick a block after the program was created. |
| `History unavailable at block N on wss://archive-rpc.vara.network (state discarded; needs an archive node)` | The node no longer holds state for that block. | Pick a more recent block. |
| `Dry run not supported by the runtime at block N` | The chain's runtime at that block predates the dry-run API. | Pick a later block. |
| `The program panics: …` | The program rejected the message at that state. The text after the colon is the program's own error. | Read it as an application error, not a HexKit or RPC fault. |
| `Block must be a number or latest` | The block field has something else in it, such as a hash or a comma. | Enter digits only, or `latest`. |
| `Origin must be 32-byte hex` | The sender under **More inputs** is not a 32-byte hex ID. | Paste a `0x…` ActorId. To convert an SS58 address, use the [Address Converter](https://hexkit.app/vara/program-tools?tool=address). |
| `Payload must be hex` | The raw payload is not `0x`-prefixed hex. | Edit **Args (JSON)** and select **Encode** instead of editing hex by hand. |

#### The call shows as hex instead of names

  HexKit could not find the program's IDL, so it cannot decode the payload. Open **Investigation → Load an IDL file…** and choose the program's `.idl` file. Both legacy Sails IDL and IDL v2 files load.

#### Snapshot A succeeds but the original message failed

  The dry run doesn't replay messages that ran before yours in the same block. If one of them changed the program's state, the original message saw different state than your replay. Open the block in Subscan from the link next to **Block** in the summary to see which extrinsics ran before it.