Explain why an Ethereum, Base, Arbitrum, Optimism or Polygon transaction failed. One-shot. Use when the user pastes a transaction hash and asks what went wrong, says a transaction failed or reverted, asks why a swap, approval, mint, claim or bridge did not work, asks what "execution reverted" or a bare 0x error selector means, or asks whether a failure was their fault. Also use when a user reports losing gas on a transaction that did nothing. Do NOT use for transactions that are still pending...
Scanned 9/6/2026
Install to Claude Code
npx -y skills add daronthedragon/tx-doctor --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of tx-doctor?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/daronthedragon-tx-doctor)More formats (shields.io, HTML) on the badges page.
---
name: tx-doctor
description: >
Explain why an Ethereum, Base, Arbitrum, Optimism or Polygon transaction
failed. One-shot. Use when the user pastes a transaction hash and asks what
went wrong, says a transaction failed or reverted, asks why a swap, approval,
mint, claim or bridge did not work, asks what "execution reverted" or a bare
0x error selector means, or asks whether a failure was their fault. Also use
when a user reports losing gas on a transaction that did nothing. Do NOT use
for transactions that are still pending and have no receipt, for questions
about what a transaction did when it succeeded, or for general contract
auditing.
---
# tx-doctor
A receipt says a transaction failed. It does not say why — revert data is not
stored on chain. This recovers it by replaying the call against the state of
the block it ran in, then names the cause.
## Procedure
1. Run `node bin/tx-doctor.mjs <hash> --json`. Add `--chain <name>` when the
user named a chain other than Ethereum; if they named none, run Ethereum and
state that assumption in your answer.
2. Read `diagnosis.cause` and `diagnosis.confidence` from the JSON. Form your
answer from those fields. Do not read the raw revert string and reach your
own conclusion.
3. Open with `diagnosis.title` rephrased in the user's own terms. Put no hex,
no selector and no gas figure in the first sentence.
4. State what the transaction was attempting, using `attempted.signature`. When
`attempted.signature` is absent, write that the function could not be
identified — do not infer it from the recipient address or the user's story.
5. Give at most three items from `diagnosis.fixes`, choosing the ones that fit
what the user described. Do not invent a fix that is not in the array.
6. When `confidence` is `low`, write that the reason could not be recovered and
name what would recover it (an archive-capable RPC endpoint). Do not present
a low-confidence cause as the answer.
7. When `cause` is `ordering-dependent`, tell the user their inputs were valid
and they lost a race. Do not tell them to change parameters.
8. When `cause` is `contract-panic` and `panicCode` is not `0x11` or `0x12`,
tell the user there is nothing they can change and name the protocol as the
party to report it to.
9. When `cause` is `out-of-gas`, state that the gas was still charged.
## What each cause means for the reader
| `cause` | Whose problem it is | Lead with |
| --- | --- | --- |
| `slippage` | The market moved | The protection worked; retry with a fresh quote |
| `insufficient-allowance` | Caller's setup | Approve the spender, then retry |
| `insufficient-balance` | Caller's funds | The balance at execution time, not now |
| `deadline-expired` | Transaction was too slow | It sat in the mempool; resubmit with a higher fee |
| `access-control` | Wrong sender | Which address is permitted |
| `paused` | Protocol admin | Nothing to fix; wait |
| `reentrancy` | Token or routing | A callback token tripped a guard |
| `ordering-dependent` | Nobody's input | They lost a race, not a validation |
| `out-of-gas` | Caller's limit | Raise the limit; the gas was still charged |
| `contract-panic` | Usually the contract | A broken invariant, not rejected input |
| `custom-error-unknown` | Unrecoverable name | The selector cannot be reversed; the ABI would name it |
| `silent-revert` | Ambiguous | Often the wrong chain or an address with no code |
| `unknown` | Not recovered | Say so, and name the endpoint that would answer |
## Never
- Never tell the user to check Etherscan for the reason. You have the reason.
- Never construct, sign, or send a replacement transaction.
- Never advise raising slippage further than `diagnosis.fixes` already does;
the array's own caution about sandwiching is part of the answer.
- Never state a cause the tool did not report.
## Example
Instead of:
> Your transaction reverted with `0x08c379a0...`. This usually means a require
> statement failed. You could try checking on Etherscan for more details, or
> increasing your gas limit and slippage to see if that helps.
Do:
> The price moved past your slippage tolerance, so the swap was rejected rather
> than filled at a worse price — the protection did its job. You were calling
> `swapExactTokensForTokens`, and it used 12% of its gas limit, so gas was not
> the issue. Confidence: high.
>
> Retry with a fresh quote. If it keeps failing on a thin pool, splitting into
> smaller trades reduces impact — widening slippage a long way is what makes
> sandwiching you profitable.
## Observable effect
Every answer names a `cause` and a `confidence` level taken from the tool's
JSON, and contains no cause the tool did not report. The phrase "check
Etherscan" does not appear.
## Deeper detail
Load only when the specific question calls for it:
- `references/failure-modes.md` — how each cause is detected, and where the
detection is wrong
- `references/recovering-revert-data.md` — why revert reasons are not on chain,
and what the replay actually does
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!