Subnets, miners, validators, consensus and the errors that cost you TAO, in plain words. Every example is a real read from finney, and every problem links to the Preflight rule that catches it
Subnets are competitions: miners do the work, validators score it, and every tempo the chain pays both in the subnet's own token
Bittensor is a blockchain that pays people to do useful work, such as running AI models, collecting data or renting out GPUs. The work is split into , each one a competition with its own task. On every subnet, do the work and check it and score it. The chain adds up the scores, weighted by how much each validator has, and pays out on a fixed clock called the . Underlined words open a short definition
1Validators send miners the subnet's task and score the answers
2They write those scores to the chain as weights, with set_weights
3Every tempo, 360 blocks or about 72 minutes, Yuma Consensus turns the stake weighted weights into incentive and dividends
4Emission is paid in the subnet's alpha: 41% to miners, 41% to validators and their stakers, 18% to the owner
Six words every other chapter builds on
Subnet
An independent competition with its own task, token and rules, numbered by its netuid
Example: SN64 Chutes, serverless AI inference
Miner
A hotkey registered on a subnet that does the work the subnet asks for
Example: UID 207 on SN64 earns 23.34 alpha a tempo
Validator
A hotkey with enough stake to hold a validator permit. It scores miners by setting weights
Example: UID 1 on SN64, 1,385,417 alpha of stake
TAO and alpha
TAO is the chain's token, 10⁹ rao each. Every subnet has its own alpha, traded against TAO, and rewards are paid in alpha
Example: A seat on SN64 costs 0.0005 TAO
UID
A numbered seat on one subnet. When every seat is taken, a new registration replaces the weakest neuron
Example: SN64 has 256 of 256 seats taken
Tempo
The subnet's clock. Once per tempo, consensus runs and everyone is paid. Blocks come every 12 seconds
Example: 360 blocks, about 72 minutes
Hotkey and coldkey: two keys, two jobs
Every participant has a pair of keys. The is the owner and holds the money, so it should never sit on a server. The is the worker: it is what registers on a subnet and what Preflight diagnoses. Read more in the network overview
A miner pays to register, gets a grace period to be noticed, earns only once validators score it, and is replaced if it earns the least
Every miner goes through the same stages: it pays a to register, sits in while validators find its , earns once they score it, and can be . Hover or tap each stage, or use the arrow keys, to see what tends to go wrong there and which Preflight rule catches it. The numbers are real SN64 reads. See the official mining guide and burn registration for the full detail
Register: burn TAO for a UID
Your coldkey pays the registration cost and your hotkey gets a UID, a numbered seat on the subnet. The TAO is recycled, not staked, so deregistering does not refund it. By default the price rises 1.26 times with each registration and halves every 360 quiet blocks
SN64 at block 9,376,731
0.0005 TAO to register, 256 of 256 seats in use
What can go wrong, and what catches it
CriticalNot registered
The hotkey holds no UID on the subnet you are checking, often because it registered on another netuid
CriticalRegistration closed
Registration is switched off, or this block's or this interval's slots are already taken
MediumBurn spiking
The burn spiked during a registration rush, so registering now pays a premium
Immunity: a grace period to get noticed
For ImmunityPeriod blocks after registering, the neuron cannot be pruned. Validators use this time to find your axon and start scoring you, so zero rewards here are normal. What matters is being reachable and scored before it ends
SN64 immunity period
5,000 blocks, about 16.7 h
What can go wrong, and what catches it
InfoIn immunity
Informational: how many blocks of immunity are left and the block it ends
HighAxon not served
No axon on chain, on a subnet where earning miners publish one, so validators have nowhere to send queries
HighAxon address unreachable
The axon points at 127.0.0.1, a LAN address or 0.0.0.0, which validators cannot reach
Scored: validators query you and set weights
Validators send your miner the subnet's task, score the answers and write the scores to the chain as weights. Only validators with a permit count, and only their latest weights
SN64 UID 0 at block 9,376,730
0 of 17 validators weighting it
What can go wrong, and what catches it
CriticalNo validator weight
No permitted validator's latest weights include your UID
HighOnly stale validators score it
Only validators past the activity cutoff score you, and the epoch ignores their weights
HighWeighted but clipped
You are weighted, but by less than kappa of the stake, so consensus clips you to zero
Earning: incentive becomes emission every tempo
Every tempo the epoch turns weights into incentive, and your share of the miner emission is paid to your hotkey in the subnet's alpha. Pruning goes by emission, so staying above the weakest neurons is what keeps the seat
SN64 UID 207 at block 9,376,730
23.34 alpha per tempo
What can go wrong, and what catches it
MediumLow trust
Trust is low: validators disagree about you, so part of your weight is clipped away
HighNear pruning
Out of immunity and among the lowest emission neurons on a full subnet
Or pruned: replaced by a new registration
When a full subnet takes a new registration, the runtime removes the neuron with the lowest emission that is out of immunity, and the older registration goes first on a tie. The hotkey loses its UID and has to register, and pay, again
SN64 at block 9,376,731
256 of 256 seats taken, so every registration prunes one neuron
What can go wrong, and what catches it
HighNear pruning
Warns before it happens: shows the pruning rank and the emission of the next neuron up
CriticalRecently deregistered
It already happened: the block it lost the seat and what it earned just before
Zero almost always comes down to one of five causes, and the Doctor checks every one of them against a single block
Check these in order, because each one hides the next: an unregistered miner cannot be reached, an unreachable miner cannot be scored, and so on. The findings below are real Doctor output, rendered with the same components as a live diagnosis
Run every check at once
$ preflight doctor <hotkey> --netuid <netuid>
1
1Cause 1: It is not registered on the subnet you are checking
No means no validator can score it. It happens more often than you would think: the hotkey registered on another netuid, or lost its seat to , which PF102 catches. The fix is to register, and the Doctor fills the command in with the live cost
Caught by
Recorded finding, a hotkey with no UID on SN64, block 9,376,731
Costs about 0.0005 TAO right now; the price moves with demand
This hotkey holds no UID on this subnet, so validators cannot score it and it earns nothing. The subnet is full, so registering replaces the lowest emission neuron that is out of immunity
Validators find miners through the address on chain. No address, 127.0.0.1, a LAN address or 0.0.0.0 all mean their queries never arrive. Not every subnet uses axons: on SN64 most earning miners publish none, so the Doctor only raises PF105 where earning miners do
Caught by
The command PF105 hands you, with your netuid filled in
Use the public address validators can reach; the miner process must listen on that port
3
3Cause 3: No validator has scored it
The most common answer. The miner is registered and may well be reachable, but none of the permitted validators' latest include it. The evidence lists the biggest validators and when each last set weights, so you can tell a quiet subnet from a miner that is being skipped
Caught by
Recorded finding, UID 0 on SN64, block 9,376,730
Fix
Check that the axon is reachable and that the miner answers the subnet's queries in the format validators expect
Incentive only comes from validators putting weight on a UID. None of the permitted validators' latest weights include this one, so it earns nothing until they do
Evidence for No validator has scored this UID
Evidence
Value
Source
Validators weighting this UID0 of 17
0 of 17
derived
UID 1 (5DU1dv...S7ww)stake 1,385,417 alpha, last set weights 18 blocks (about 4 min) agoSubtensorModule.LastUpdate
stake 1,385,417 alpha, last set weights 18 blocks (about 4 min) ago
SubtensorModule.LastUpdate
UID 252 (5E2LP6...eZ5u)stake 907,925 alpha, last set weights 138 blocks (about 28 min) agoSubtensorModule.LastUpdate
stake 907,925 alpha, last set weights 138 blocks (about 28 min) ago
SubtensorModule.LastUpdate
UID 59 (5CS3g6...2xbV)stake 765,922 alpha, last set weights 18 blocks (about 4 min) agoSubtensorModule.LastUpdate
stake 765,922 alpha, last set weights 18 blocks (about 4 min) ago
SubtensorModule.LastUpdate
UID 46 (5CAAxT...pQ1C)stake 336,362 alpha, last set weights 3 blocks (about 36 s) agoSubtensorModule.LastUpdate
stake 336,362 alpha, last set weights 3 blocks (about 36 s) ago
SubtensorModule.LastUpdate
UID 61 (5GRbxc...CdXc)stake 205,685 alpha, last set weights 20 blocks (about 4 min) agoSubtensorModule.LastUpdate
stake 205,685 alpha, last set weights 20 blocks (about 4 min) ago
4Cause 4: It is scored, but consensus clips it to zero
Some validators weight it, but together they hold less than of the stake, so Yuma Consensus throws the weight away. Chapter 4 lets you try it with real SN64 stakes
Caught by
What PF110 tells you to do
Fix
More of the large validators need to score this UID; check it answers their queries
5
5Cause 5: The subnet itself is not paying miners
Sometimes the miner is fine and the subnet is not. On SN26 at this block, three of its five validators had stopped setting weights and the owner's burn UID took all of the miner incentive, so a new miner there had nothing to win. Preflight reports these as signals about the subnet, never as verdicts
A weight only counts as far as validators holding kappa of the stake agree with it, so one validator alone cannot make a miner earn
Validators do not simply vote a miner up. Each , looks at every weight on a miner and keeps only as much of it as validators holding at least of the stake agree on. Anything above that is clipped. If too little stake weights a miner at all, its consensus weight is zero and so is its incentive, even though some validators scored it
Try it below with the five largest validators on SN64. Switch validators on and off, move kappa, and watch whether the miner's weight survives. This is the exact check behind PF110
Simplified model of Yuma ConsensusStakes: the five largest SN64 validators, block 9,376,730
50%
Finney subnets typically run 50%. SN64 reads 0.5
Validator stake that weighted the miner
kappa 50%
0%36.3% weighted it100%
Clipped to zero, the miner earns nothing
Only 36.3% of the stake weighted it, below kappa (50%). Consensus clips the weight to zero, so incentive is 0. This is the case PF110 reports
The real epoch is finer grained: it clips each weight to the highest value that kappa of stake supports, so validators who agree a miner is good but disagree on how good still lose some weight. That partial loss shows up as low , which is what PF111 watches. The full model is in Yuma Consensus
A validator needs enough stake for a permit, must set weights often enough to stay active and no faster than the rate limit
A validator earns for scoring miners well. To do that it needs stake, a and weights that reach the chain on time, inside the . Most validator problems are one of the checks below failing, and the chain error names which one. See the validating guide and timelock for
01Stake
Alpha staked to the hotkey, plus discounted TAO
02Permit
Top stake weight on the subnet, recomputed each epoch
03Set weights
Score miners, then set_weights or commit them
04Stay active
Fresh weights inside the activity cutoff
Steps 3 and 4 repeat for as long as the validator runs, no faster than the rate limit and no slower than the cutoff
Five checks every set_weights call must pass
CheckHyperparameter, valueChain error when it failsRule
Permit
MaxAllowedValidators
64 permits on SN64
Each epoch the top neurons by stake weight get a permit. Without one, weights on other UIDs are refused
NeuronNoValidatorPermit
Stake
StakeThreshold
about 1,000 TAO equivalent on mainnet
The hotkey's stake weight must reach the threshold. The subnet owner's hotkey is exempt
NotEnoughStakeToSetWeights
Version
WeightsVersionKey
0 on SN64, so not enforced
When the owner sets a version key, validators must send at least that version
IncorrectWeightVersionKey
Rate limit
WeightsSetRateLimit
100 blocks on SN64, about 20 min
A validator must wait this many blocks after its last weights before sending new ones
SettingWeightsTooFast
Shape
MinAllowedWeights, MaxWeightsLimit
1 UID and 100% on SN64
Weights must cover enough UIDs and keep each single weight under the cap
WeightVecLengthIsLow, MaxWeightExceeded
SN64 values from its hyperparameters as recorded at block 9,376,750. The stake threshold is from the validating guide
After they land: what decides whether weights count
Activity cutoff
5,000 blocks on SN64, about 16.7 h
No error, which is why this one hurts: weights older than the cutoff are silently ignored and earn no dividends
Commit reveal
Off on SN64; up to 10 unrevealed commits per hotkey where it is on
Weights are encrypted to a future drand round and only count once the chain reveals them
Self weighting
Half or more of the weight to its own coldkey
Looks like self mining. A signal, never a verdict
Copier signature
Correlation above 0.98 with last epoch's consensus
Looks like weight copying, which commit reveal exists to stop. A statistical signal only
A healthy validator with a finding: UID 1 on SN64 must wait 82 blocks
This validator set weights 18 blocks before the read. The rate limit on SN64 is 100 blocks, so a new call would fail with SettingWeightsTooFast until block 9,376,812. That is information, not a fault: the fix is to wait, and retrying in a tight loop only burns time
What stale looks like: SN26 validators3 of 5 past the 720 block line, so PF305 fires
720 blocks
UID 143
26 blocks, about 5 min
26 blocks, about 5 min
UID 0
88 blocks, about 18 min
88 blocks, about 18 min
UID 152
49,757 blocks, about 6.9 days
49,757 blocks, about 6.9 days
UID 45
955,951 blocks, about 132.8 days
955,951 blocks, about 132.8 days
UID 36
1,003,768 blocks, about 139.4 days
1,003,768 blocks, about 139.4 days
101001k10k100k1M
Blocks since each permitted validator last set weights, read at block 9,376,730 (the PF108 evidence for UID 82 on SN26), on a log scale. PF305 calls a validator stale after max(2 tempos, rate limit plus a tempo), here 720 blocks or about 2.4 h
Every failed extrinsic names the pallet and the error that stopped it; the rest is reading the chain state it was checked against
This is a real validator on finney that failed. Every failure names the and the error that stopped it. Here is how to read one, top to bottom, the same way the transaction debugger does
1
Find it
Explorers name an extrinsic by its block and its position in that block. 9376733-28 is the 29th extrinsic in block 9,376,733, since the index starts at 0. Preflight takes that form, a 0x hash from the last 300 blocks, or the error text you were shown
The signer is a validator's , because weights are signed by the hotkey. The call is commit_timelocked_mechanism_weights: on a commit reveal subnet, validators commit weights encrypted until a future round instead of setting them in the open
Each argument is decoded from the raw call. The ones that matter here are the subnet, and the round that tells the chain when it may decrypt the commit
Decoded arguments
netuid
SN28
The subnet, SayGM in the directory
mecid
0
Mechanism id. A subnet can run more than one incentive mechanism; 0 is the first
commit
0xa7ef6501... (869 bytes)
The weights, encrypted so nobody can read or copy them yet
reveal_round
33441416
The drand round whose pulse lets the chain decrypt them
commit_reveal_version
4
The commit format version
4
Read the events in order
The fee is charged first, then the runtime dispatches the call. ExtrinsicFailed carries the module error that stopped it. The fee was 0 TAO, so the only cost was a commit that never landed
Events
TransactionPayment.TransactionFeePaid
Fee paid 0.000000000 TAO
System.ExtrinsicFailed
Failed with SubtensorModule.TooManyUnrevealedCommits
5
Decode the error
TooManyUnrevealedCommits comes from the official error catalog with its semantic code and the line in subtensor that raises it. The catalog says the limit: 10 unrevealed commits per hotkey on a subnet
Dispatch error
SubtensorModule.TooManyUnrevealedCommits
Code
limit_exceeded
Raised at
pallets/subtensor/src/macros/errors.rs#L180
6
Explain it with its own numbers
The debugger rereads the chain at the block before, so the explanation names this hotkey's queue, not a generic limit. Timelocked commits reveal themselves when their drand round arrives, so the answer is to wait, and commit less often. shows the same queue in the Doctor before it fills
Why it failed
Before this block the hotkey already had 10 unrevealed commits queued on the subnet, so the runtime refused another
Wait
Let the queued commits reveal (timelocked commits reveal on their drand round) before committing again
Read from finney at block 9,376,733, runtime 486. Recorded in docs/demo-output/tx-mainnet.json
The simulator runs your extrinsic against a copy of mainnet, so you see the exact error before you spend a single rao
A failed registration still costs its fee, and the burn can move between your quote and your retry. The simulator answers “would this work right now” without sending anything, by running the chain's own on a or predicting from the same rules. It always says which one it used
A real run: registering on SN64 with zero TAO
Each line is one condition the chain tests, with the value it read at block 9,377,481. Two fail, so the chain would reject the call before it runs and nothing would be spent
Would failburned_registeron SN64, from a coldkey with 0 TAO
Balance covers the fee0.000000 TAO free for a 0.001071 fee
Registration openNetworkRegistrationAllowed is True
Per block cap0 of 1 used this block
Per interval cap0 of 3 used this interval
Hotkey not already registeredno UID yet
Balance covers burn and fee0.0000 TAO free, needs 0.0005 burn + 0.001071 fee
Executed on a fork of block N with the real runtime
Preflight copies mainnet at one block onto your machine and runs your transaction through the chain's own code. You get exactly the answer the chain would give, and then the copy is thrown away, so nothing is ever sent
Runs in
The CLI, the MCP server and preflight serve on your machine
Under the hood
A Chopsticks fork, the runtime's BlockBuilder_apply_extrinsic, and a mock signature so any address works
Predicted
Predicted from live state and runtime rules
Preflight reads the live numbers that decide the outcome, such as the burn, your balance and the registration limits, and runs the same checks the chain runs, copied from its source code
Runs in
The hosted site, which cannot keep a fork running
Under the hood
Pallet checks ported from subtensor, each cited by file and line
Why not the SDK's dry run: plan() says ok, the chain says no
The bittensor SDK has client.plan(), which returns fees and effects. It never runs the call, so it said ok to all three of these. Preflight uses it for fee and effect text only, never for the verdict
Intent on SN64SDK plan()What the chain does
BurnedRegister from a coldkey with 0 TAOplan().ok = true
ok: trueThe chain says Rejected: cannot pay fees
SetWeights signed by a miner hotkey with no permitplan().ok = true
What the fork returned on finney, with timingsFive measured runs from docs/decisions/001-simulator.md
Action on SN64SetupFork resultTime
burned_registerColdkey with 0 TAOErr(Invalid(Payment))10.8 s, first use
burned_registerBalance overridden to 10 TAOOk222 s cold, about 2 s once cached
burned_registerBalance overridden to 0.0001 TAOErr(Invalid(Payment))1.8 s
serve_axonAxon at 127.0.0.1InvalidIpAddress9.0 s
start_callA stranger with 0 TAOErr(Invalid(Payment))4.5 s
Fork of finney block 9,376,889. The first call on a new path fetches storage lazily, which is why it can take minutes; requests fall back to the prediction after 20 s while the fork warms
A subnet costs a TAO lock that doubles after each registration and decays back, and at 128 subnets a new one replaces another
Launching a subnet means paying the , waiting for the network , possibly replacing another subnet, and calling before anyone earns. These are the real numbers from finney at block 9,377,485
Lock cost
346.37TAO
Last paid 224.64 TAO; never below the 1 TAO floor
Rate limit
14,400blocks
One subnet per 2 days. Next allowed at #9,378,690
Subnet cap
128/ 128
At the cap: registering would dissolve SN16 Zero Hour
Network immunity
60days
432,000 blocks before a new subnet can be replaced
Lock cost if nobody registers, from block 9,377,485about 0.0078 TAO less every block
0
100
200
300
346.37 TAO now
336.97 TAO at the next allowed block
floor, 1 TAO at block 9,421,762
#9,377,485#9,427,762
At block 9,377,485 the lock cost was 346.366 TAO. With no new registrations it falls about 0.0078 TAO per block, to about 336.9668 TAO at block 9,378,690 and to the 1 TAO floor at block 9,421,762
From docs/demo-output/plan.txt. Each subnet registration doubles the cost for the next one, so on a live chain this line jumps back up every time someone registers, then decays again
From registration to first emissions, with real blocks
+361 blocks, 1.2 h
Register and start_call
#9,378,690
The first block the 14,400 block rate limit allows. Registering locks the cost and makes you owner; on finney start_call can follow at once
then +361 blocks, 1.2 h
+431,639 blocks, 60 days
First emissions
#9,379,051
Paid by the first epoch after start_call, within one tempo of 360 blocks
then +431,639 blocks, 60 days
Immunity ends
#9,810,690
From here the subnet can be replaced when a new one registers at the cap
At 128 subnets, a new registration dissolves the subnet with the lowest moving alpha price among those past their immunity. At this block that was SN16 Zero Hour. The planner names it before you pay, along with the exact block the rate limit lifts
Launch from the CLI
# What it costs and when you can register$ preflight plan --network finney$ btcli subnets create-cost# After registering, start emissions (owner coldkey only)$ btcli hparams start --netuid <netuid> --network finney
Paste a hotkey, a transaction or an error. Preflight reads the chain at one block and tells you which of the problems above you have, with the command that fixes it. Below is reference: every rule, every way to run Preflight, and a glossary
27 rules, each a small pure function over one block of chain state, grouped by who they are for
Every finding the Doctor returns comes from one of these rules. Each reads a single pinned block, cites the storage item it read, and ships with tests for when it fires and when it does not. Link to any row with its id, like /guide#pf110
One engine answers the CLI, the website, the HTTP API, your own Claude, CI and Telegram, with the same JSON
Install once with uv, then ask from any terminal. Add --json for machine output and --fail-on high to fail a script when something is wrong
Install and run
$ uv tool install git+https://github.com/Adwaitbytes/preflight$ preflight doctor 5EKtvzbEvYZPRJdo42ug7mWrHdt4AurCNMs5CAefuBHfQmhq --netuid 64$ preflight tx 9376733-28$ preflight plan
Real output, docs/demo-output/doctor-mainnet.txt
BROKEN UID 0 on SN64 earns nothing because no validator has scored itfinney at block 9,377,476, runtime 486 4,451 ms CRITICAL PF108 No validator has scored this UID Validators weighting this UID 0 of 17 UID 1 (5DU1dv...S7ww) stake 1,385,572 alpha, last set weights 163 blocks (about 33 min) ago UID 252 (5E2LP6...eZ5u) stake 908,518 alpha, last set weights 164 blocks (about 33 min) ago