Runtime upgrades are powerful, and easy to get subtly wrong. The PAPI team is used to deal with issues that arise due to faulty compiled runtimes, which is why we decided to create a tool to help everyone check for these common mistakes.
We are happy to introduce @polkadot-api/check-runtime, a tiny library and CLI that detects a handful of common, high-impact mistakes before (or after) you roll out a runtime upgrade.
- Library: call
getProblems(uri, { wasm, block, token })and get back a list of issues. - CLI: run
check-runtime problems <uri>locally or in your CI; it prints problems and exits with1if anything is wrong.
If you want just the TL;DR:
# check a live chain at head
npx @polkadot-api/check-runtime problems wss://your.chain.rpc
# check a specific height
npx @polkadot-api/check-runtime problems wss://your.chain.rpc --at 123456
# check using a local or remote WASM (pre-deploy sanity check)
npx @polkadot-api/check-runtime problems wss://your.chain.rpc --wasm ./runtime.wasm
npx @polkadot-api/check-runtime problems wss://your.chain.rpc --wasm https://example.com/runtime.wasm
Why this exists
A few incidents keep recurring across Polkadot SDK chains:
- Missing Runtime APIs → clients can’t query/derive critical info.
- Mismatched/incorrect
CheckMetadataHashsigned extension → offline or strict signers produce transactions that the runtime rejects. - Inconsistent metadata hashes between v15 and v16 → subtle client misbehavior.
These are the kind of issues you want your tooling to scream about immediately -ideally in CI- so you never ship a runtime that looks fine locally but fails in the wild.
What it checks (Problem reference)
The tool returns a list of problem identifiers. Here’s what each one means:
| Problem | What it means | Why it matters |
|---|---|---|
ANCIENT_METADATA |
The runtime doesn’t expose modern (≥ v14) metadata. | Many SDKs/wallets rely on modern metadata; you’ll hit compatibility issues. |
MISSING_MODERN_METADATA |
The runtime only exposes metadata v14 (no v15/v16). | Certain functionalities on modern libraries are only available with Metadata v15+. |
MISSING_RUNTIME_APIS |
Required Runtime APIs are missing. | A common issue that’s very painful for DApp developers and fairly easy to solve. |
MISSING_CHECK_METADATA_HASH_EXTENSION |
The extrinsic format lacks the CheckMetadataHash signed extension. |
Especially problematic for signing with hardware wallets. |
WRONG_OR_MISSING_METADATA_HASH |
The runtime wasn’t compiled with the correct metadata hash. | Transactions that correctly use the CheckMetadataHash will be invalid. E.g: transactions created with Ledger. |
DIFFERENT_METADATA_HASHES |
The metadata hash differs between v15 and v16. | Indicates an internal inconsistency. There is no easy fix for this problem yet, it’s being worked on, but it’s a problem nonetheless. |
The CLI prints human-readable messages for each and exits with code 1 if anything’s off.
Installation
Library + CLI (recommended in repos/CI):
npm i -D @polkadot-api/check-runtime
# or
pnpm add -D @polkadot-api/check-runtime
# or
yarn add -D @polkadot-api/check-runtime
One-off via npx:
npx @polkadot-api/check-runtime problems wss://your.chain.rpc
Requires Node.js 22+.
Using the CLI
check-runtime problems <uri> [options]
<uri>: WebSocket RPC, e.g.wss://rpc.polkadot.io.
Options
--wasm <filenameOrUrl>: local path or URL to a runtime WASM to check against.--at <block>: height or block hash to evaluate at (applies/inspects the WASM in that context if provided).--symbol <symbol>: override token symbol (defaults to RPC chain spec).--decimals <decimals>: override token decimals (defaults to RPC chain spec).
Examples
# check live chain at head
npx @polkadot-api/check-runtime problems wss://rpc.polkadot.io
# check a specific height
npx @polkadot-api/check-runtime problems wss://rpc.polkadot.io --at 19000000
# check using a locally built WASM
npx @polkadot-api/check-runtime problems ws://localhost:9944 \
--wasm ./artifacts/runtime.compact.compressed.wasm \
--at 123456
# check against a WASM from a URL
npx @polkadot-api/check-runtime problems wss://rpc.my-chain.io \
--wasm https://example.com/my-runtime.wasm
# override token info (e.g. dev chains)
npx @polkadot-api/check-runtime problems ws://localhost:9944 \
--symbol TST --decimals 12
Exit codes
0– no problems found (“Everything looks great!”)1– one or more problems were detected (messages printed to stderr)
This makes it straightforward to block a CI/CD stage if a runtime is unsafe to ship.
Programmatic API
The library exposes a single function:
import { HexString } from 'polkadot-api';
type Problem =
| 'ANCIENT_METADATA'
| 'MISSING_MODERN_METADATA'
| 'MISSING_RUNTIME_APIS'
| 'MISSING_CHECK_METADATA_HASH_EXTENSION'
| 'DIFFERENT_METADATA_HASHES'
| 'WRONG_OR_MISSING_METADATA_HASH';
declare function getProblems(
uri: string,
options?: Partial<{
wasm: HexString;
block: HexString | number;
token: Partial<{ symbol: string; decimals: number }>;
}>
): Promise<Array<Problem>>;
CI integration
GitHub Actions
name: Runtime checks
on: [push, pull_request]
jobs:
check-runtime:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npx @polkadot-api/check-runtime problems ${{ secrets.RPC_URI }} --at 123456 --wasm ./dist/runtime.wasm
GitLab CI
runtime_check:
image: node:22
script:
- npx @polkadot-api/check-runtime problems $RPC_URI --at 123456 --wasm ./dist/runtime.wasm
Notes & limitations
- The tool does not submit extrinsics and does not mutate on‑chain state; it uses Chopsticks behind the scenes.
--at/blockaccepts either a height (number) or a block hash (hex string).- When
--wasm/wasmis provided together with--at/block, the height is used as the context at which to check and/or apply that specific code. - If
symbol/decimalsare not provided, they are read from the chain-spec via RPC. Keep in mind that’s not “on-chain” data, ie: it’s safer to provide these values.
Call for feedback
If this saves you from a messy rollback, or if you need another check added: please comment here or file an issue.
Happy shipping!