3MIKAN
仮想通貨直コン

Ethereum RPCエラーを層別に直す:4001・429・-32602・nonce・revert

Ethereum RPCエラーをwallet provider、HTTP、JSON-RPC、node、transaction、EVMへ分類。viemのcause chainとraw responseを対応させ、安全な再試行と修正手順を解説します。

3MIKANのブランドキャラクターが一つのオレンジ色の信号を6段階の診断装置に通し、エラーが止まった層を確認する記事画像

4001429-32602nonce too lowexecution revertedを、すべて「RPCエラー」として同じ再試行へ流してはいけません。発生した場所が違えば、直す対象も違います。

最初に保存するのは、method、paramsの形、chain ID、block、HTTP status、JSON-RPCのcode・message・data、viemのerror名とcause、transaction hashです。API key、cookie、署名済みraw transaction、個人情報は保存しません。

この記事では、wallet provider、HTTP transport、JSON-RPC protocol、nodeまたはgateway policy、transaction validation、EVM executionへ分けます。2026-08-31時点のviem 2.56.0とFoundry / Anvil 1.8.0を使い、外部walletやpublic RPCを使わないローカル環境で12種類の応答とcustom errorを確認しました。

エラーを7層へ置く

画面へ最後に表示されたmessageは、もっと内側のerrorを包んでいることがあります。次の順で、最初に失敗した境界を探します。

アプリとviem、wallet provider、HTTP transport、JSON-RPC、node policy、transaction validation、EVM executionの7層。各層で保存する証拠と次の確認先を示す
代表的な症状 最初に保存する証拠 主な次の行動
app / library ChainMismatchError、ABIや型の不一致 action名、library version、入力の形 chain、ABI、引数を直す
wallet provider 40014100420049004901 origin、method、provider code、選択chain user action、許可、対応methodを確認
HTTP transport DNS、TLS、timeout、4295xx URLのhost、status、Retry-After、経過時間 待機、範囲縮小、限定的なfallback
JSON-RPC -32601-32602-32603 request ID、method、params、error object method・params修正、内側のerror確認
node / gateway -32005、archive不足、payload制限 client、plan、block range、message、data range縮小、必要なnode機能を選ぶ
transaction validation funds、fee、nonce、replacement sender、nonce、balance、fee、既存hash 同じ入力の連打を止めてstateを直す
EVM execution revert、out of gas、custom error call data、revert data、シミュレーション時のblock、receipt selectorと引数を復号し、contract条件を直す

codeだけで層を断定できない場合もあります。たとえば-32000はEIP-1474でinvalid inputですが、node実装はmessageへinsufficient fundsなどを入れることがあります。code、message、data、呼び出したactionを組にしてください。

最初の30秒でrequest identityを固定する

再現前に、次を一件の記録へまとめます。

  1. operation名と実行時刻
  2. RPC methodと、秘密値を除いたparamsの形
  3. chain ID、block numberまたはtag、対象address
  4. HTTP status、Retry-After、経過時間
  5. JSON-RPC code、message、dataの型
  6. viemの最上位error名、shortMessagedetails、cause名
  7. transactionを送った可能性がある場合はhash、sender、nonce

methodとparamsを保存せずmessageだけを見ると、同じInvalidParamsRpcErrorでも「addressの形式」「block quantityの形式」「引数の個数」を区別できません。反対にraw headerを丸ごと保存すると、API keyやcookieを漏らす危険があります。必要なfieldを先に決め、値ではなくshapeだけ残すfieldを分けます。

wallet providerの4001・4100・4200・4900・4901

EIP-1193は、providerが返す代表的なcodeを定義しています。

code 意味 自動再試行 読者へ伝える内容
4001 userがrequestを拒否 しない キャンセル済み。必要なら本人がもう一度開始する
4100 methodまたはaccountが未承認 しない originとaccountの許可を確認する
4200 providerがmethod非対応 しない 対応確認または別の実装経路が必要
4900 すべてのchainから切断 接続回復後だけ providerの接続状態を確認する
4901 指定chainだけ未接続 しない 意図したchainを確認し、明示的に選択する

4001を「RPCが不安定」と扱って自動でwallet popupを再表示すると、userの拒否を無視します。4200も時間経過ではmethod対応になりません。4901では、アプリが別chainへ勝手に目的を変えず、要求したchainと現在chainを表示します。

chainの値がアプリ設定とwallet clientで違うChainMismatchErrorは、providerへrequestが届く前にlibraryが止める場合があります。このときはHTTP statusを探すのではなく、設定したchain、walletが選んだchain、署名対象chainを比較します。

HTTP 429・timeout・5xxは送信結果を先に分ける

HTTP 429 Too Many Requestsは、一定時間のrequest数が多すぎることを示します。RFC 6585では、serverは待機時間をRetry-Afterで示せます。

読み取りmethodなら、Retry-Afterを優先し、上限付き指数backoffとjitterを使えます。たとえば1秒、2秒、4秒へ増やし、最大回数と全体timeoutを決めます。全clientが同時に再試行するのを避けるため、待機へ小さな乱数を加えます。

しかしeth_sendRawTransactionのtimeoutは別です。responseを受け取れなくても、nodeがtransactionを受理した可能性があります。同じ署名済みtransactionならhashを手元で導出し、まずeth_getTransactionByHashとsenderのpending nonceを確認します。別nonceや別署名を作って即送信すると、重複意図やreplacement競合を増やします。

状況 同じrequestの再試行 先に行う確認
eth_blockNumberがtimeout 上限付きで可 endpoint、経過時間、chain ID
eth_getLogsが429 range縮小後に可 Retry-After、from / to block、件数制限
eth_callが503 block固定後に可 chain、block、calldata、fallbackとの差
eth_sendRawTransactionがtimeout 即時は不可 導出hash、pending nonce、既存transaction
wallet署名要求がtimeout 自動表示しない wallet側の未完了requestとuser action

fallback endpointを使う場合も、chain、block、method、paramsを変えません。別endpointで値が違うときのblock固定方法はEthereum RPCのlatest・archive・Multicallを比較する記事で確認できます。

JSON-RPCの-32601・-32602・-32603

EIP-1474はEthereum RPCのerror objectにcodeとmessageを要求し、JSON-RPC標準codeを採用しています。

  • -32601 Method not found: method名が存在しない、またはendpointが公開していない
  • -32602 Invalid params: 引数の個数、順序、型、hex encodingなどが不正
  • -32603 Internal error: JSON-RPC処理内の内部error

-32601-32602は、同じbodyを待って再送しても通常は直りません。methodの綴り、clientの対応、address、quantityとdataのhex規則、block identifier、引数の順序を直します。

-32603は一時的な場合がありますが、先にraw errorのdataと内側のmessageを保存します。contract revertをgatewayがinternal errorへ包む実装もあるため、「node障害」と断定して再試行する前にrevert dataがないか確認します。

{
  "jsonrpc": "2.0",
  "id": 17,
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": { "field": "params[0]" }
  }
}

request IDはresponseとの対応に使います。batch responseは送信順と同じとは限らないため、配列位置ではなくIDで結合します。

node policyはgatewayとexecution clientを分ける

-32005 Limit exceeded、広すぎるeth_getLogs、古いstateへのarchive制限、payload size制限は、同じnode故障ではありません。provider plan、gateway、load balancer、execution clientのどこが返したかを記録します。

range制限ならblock範囲を分割し、重複blockをIDやlog identityで排除します。archive不足なら、同じcurrent requestが成功してもhistorical stateを読める証拠にはなりません。必要なhistory depthを明示し、対応するnodeまたはplanを選びます。

providerを切り替えるだけで万能に直るとは限りません。invalid paramsやcontract revertを別providerへ繰り返せば、同じ入力を増幅するだけです。

insufficient funds・nonce・replacementはstateを直す

transaction validationは、EVM bytecodeを実行する前に止まる層です。go-ethereum 1.17.5のsourceでも、nonce、funds、gas、fee cap、transaction poolのreplacement policyは別errorとして定義されています。

insufficient funds

sender balanceがvalue + gas limit × feeを賄えるか確認します。token残高ではなく、gasを支払うnative assetの残高です。同じtransactionを待って再送しても残高は増えません。balanceを読むblock、value、gas、max feeを揃えて再計算します。

nonce too low

指定nonceがすでに使われた、同じtransactionが取り込まれた、または別endpointのpending viewが遅れている可能性があります。latestpendingのtransaction count、既知hash、receiptを確認します。nonceを機械的に1増やす前に、同じ意図がすでに送られていないか調べます。

replacement underpriced

同じsender・nonceのpending transactionを置き換えるには、nodeのreplacement policyを満たすfee上昇が必要です。値はclientやpool policyで変わり得るため、固定率を万能値として書きません。元transaction、replacement、同じnonce、fee field、node messageを対応させます。

transactionの入力・receipt・eventを調べる全体手順はviemで失敗transactionを調べる記事、gas estimationの境界はestimateGasが失敗する理由、pending・dropped・replacedはtransaction状態を判定する記事へ進んでください。

execution revertedはEVMの戻りdataを読む

execution revertedは、EVM executionへ到達して戻された状態です。HTTP 200のJSON-RPC errorになることも、-32603-32000へ包まれることもあります。messageだけでなく、revert dataを保存します。

custom errorなら先頭4 bytesのselectorとABIからerror名と引数を復号できます。ローカルAnvilではexecute(3)に対してBelowMinimum(caller, 3, 7)を返し、viemでは最上位のContractFunctionExecutionErrorからcauseをたどってContractFunctionRevertedErrorと引数を取得しました。revert後にcontract stateが変わっていないことも確認しています。

revert dataの復号手順はexecution revertedとcustom errorの記事を参照してください。シミュレーションが成功しても、送信時のblock、state、sender、value、gasが変われば同じ結果とは限りません。反対にシミュレーションのrevertはreceiptではなく、transactionがbroadcastされた証拠にもなりません。

viemではshortMessage・details・causeを分ける

viem 2.56.0ではerrorがBaseErrorを継承し、読みやすいshortMessage、内側のdetails、元errorのcauseを持ちます。最上位messageを正規表現だけで分類せず、classとcause chainを使います。

import {
  BaseError,
  HttpRequestError,
  InvalidParamsRpcError,
  RpcRequestError,
} from 'viem'

try {
  await client.getBalance({ address, blockNumber })
} catch (error) {
  if (!(error instanceof BaseError)) throw error

  const httpError = error.walk(
    (candidate) => candidate instanceof HttpRequestError,
  )
  const rpcError = error.walk(
    (candidate) => candidate instanceof RpcRequestError,
  )
  const invalidParams = error.walk(
    (candidate) => candidate instanceof InvalidParamsRpcError,
  )

  audit.write({
    name: error.name,
    shortMessage: error.shortMessage,
    details: error.details,
    httpStatus: httpError?.status,
    rpcCode: rpcError?.code,
    rpcDataShape: shapeOnly(rpcError?.data),
    decision: invalidParams ? 'fix-input' : 'review-layer',
  })
}

detailsにはnode messageが入ることがありますが、秘密値を含まない保証はありません。公開logへ出す前にredactionを通します。Authorization、cookie、API key付きURL、署名済みraw transaction、private key、seed phraseは記録しません。

ローカル再現で12種類のmappingを固定した

Node 24.14.0とviem 2.56.0で、127.0.0.1の一時HTTP serverだけを使い、次を確認しました。

  • provider: 40014100420049004901
  • transport: HTTP 429 + Retry-After: 2、40ms timeout
  • JSON-RPC: -32601-32602-32603
  • node policy: -32005
  • transaction validation message: -32000 + insufficient funds

たとえば-32602InvalidParamsRpcError → RpcRequestError → raw error object、HTTP 429はHttpRequestError、4001はUserRejectedRequestError → RpcRequestError → raw error objectになりました。公開再現JSONで、sanitized request、raw response、viem error chain、再試行分類を確認できます。

Foundry / Anvil 1.8.0、Solidity 0.8.36、Prague EVMではcustom errorとrollbackを3ケース確認しました。外部wallet、public RPC、real account、外部署名、実資産は使っていません。

再試行・backoff・fallbackの判断

request identityを保存し、送信済み可能性を判定し、error layerを特定してから入力修正・user action・限定再試行・fallbackへ分岐する手順
  1. method、params shape、chain、block、request IDを固定する
  2. transaction送信済みの可能性があるか分ける
  3. HTTP statusとJSON-RPC error objectを別fieldへ保存する
  4. viemの最上位classとcause chainから失敗層を特定する
  5. user拒否、unsupported、invalid params、funds、nonce、revertは原因を直す
  6. 429、timeout、5xx、internal errorは、読み取りだけ上限付きで再試行する
  7. writeの結果が不明ならhashとnonceを調べ、重複送信を防ぐ
  8. fallbackでもchain、block、method、paramsを保ち、応答差を記録する

自動再試行の対象をcode一覧だけで決めず、methodの副作用と「serverが受理した可能性」を判定してください。eth_calleth_sendRawTransactionは、同じtimeoutでも扱いが違います。

support logの最小field

field 保存例 秘密値への対応
operation load-position 内部画面名を一般化
method eth_call 保存可
sanitizedParamsShape {to,dataBytes,blockTag} calldata全文は必要時だけ限定保管
chainId / block 1 / finalized 保存可
httpStatus / retryAfter 429 / 2 保存可
rpcCode / rpcMessage -32602 / Invalid params dataに個人情報がないか確認
viemErrorName InvalidParamsRpcError versionと組にする
causeNames class名の配列 message全文より先に保存
transactionHash 既知の場合のみ raw signed transactionは保存しない
attempt / elapsedMs 2 / 2140 再試行の集中を発見する
decision fix-input 実行した分岐を残す

RPCや監視serviceを比較する場合も、この分類と計測条件を広告・sponsor表記から分離します。特定providerへの切替を、invalid input、nonce競合、contract revertの万能解決策として扱いません。

確認チェックリスト

  • methodとparams shapeを保存した
  • chain ID、block、addressを固定した
  • HTTP statusとJSON-RPC codeを別fieldで読んだ
  • viemのshortMessagedetails、cause classを確認した
  • 4001を自動で再表示していない
  • 429ではRetry-Afterと範囲制限を確認した
  • invalid paramsを待機だけで再送していない
  • transaction送信結果が不明ならhashとnonceを先に調べた
  • insufficient funds、nonce、replacement、revertを別層へ分けた
  • fallbackでもchain、block、method、paramsを変えていない
  • API key、cookie、署名済みraw transaction、秘密鍵をlogへ残していない

結論は、error messageをそのまま再試行条件にせず、raw responseとviem cause chainを対応させ、失敗した層とmethodの副作用を決めてから次の行動を選ぶことです。入力を直すerror、user actionを待つerror、限定的に再試行できるerrorを分ければ、無駄なrequestと重複transactionを減らせます。

確認した一次情報