JSON-RPC error -32601 (Method not found) is a pre-defined code from the JSON-RPC 2.0 specification returned when a method does not exist or is not available on that endpoint. In practice it has two distinct causes: a genuinely unknown method name (typo, wrong chain dialect, client-fork-specific method) or a method that exists in the client but is deliberately not exposed by the endpoint, such as a disabled debug, trace, admin, or txpool namespace. Because public and shared endpoints commonly disable expensive namespaces for cost and security reasons, -32601 is usually a capability statement about the endpoint rather than a bug in your code. This article explains the namespace model, shows a runnable Node.js capability probe, and gives a results table, decision guide, and troubleshooting checklist so you can discover what an endpoint actually supports instead of guessing.
The -32601 Error Code in the JSON-RPC 2.0 Specification
The JSON-RPC 2.0 specification defines a small set of pre-defined error codes in Section 5.1. Code -32601 is reserved for "Method not found" and is returned when the requested method does not exist or is not available. That single sentence carries both causes you will encounter in blockchain RPC: the method name may be unknown to the server, or the method may exist in the server software but be unavailable to your request.
The specification does not require the server to explain which of those two situations applies. The error object may include a data field with extra detail, but many implementations return only the code and a short message. This is why the same call can succeed against one endpoint and fail with -32601 against another: the code describes the endpoint's response, not the validity of your application logic.
The Ethereum JSON-RPC specification builds on this base by grouping methods into namespaces such as eth, net, web3, debug, trace, txpool, and admin, with additional namespaces on some L2 and Parity-derived clients. Namespace membership is a convention, not a guarantee of exposure. An endpoint can implement the eth namespace fully while returning -32601 for every debug and trace call.
- Authoritative source: JSON-RPC 2.0 specification, Section 5.1 (Error object and pre-defined codes).
- Authoritative source: Ethereum JSON-RPC specification (method namespaces and method definitions).
- -32601 means "not found or not available" — it does not distinguish the two.
Two Distinct Causes That Both Surface as -32601
The first cause is a genuinely unknown method name. This includes typos and casing mistakes, calling a method that belongs to a different chain's dialect, or calling a method that only exists on a specific client fork. For example, a method added by one execution client may not exist in another, and an L2 may expose a namespace that the L1 does not. If the name is wrong, no endpoint configuration will fix it.
The second cause is a method that exists in the client software but is deliberately not exposed by the endpoint. Operators disable namespaces for cost, security, and stability reasons. A debug or trace call can be orders of magnitude more expensive than a simple read, and admin methods can change node state. When a namespace is disabled at the gateway or node configuration level, the method is effectively invisible and the server correctly returns -32601.
Distinguishing the two matters because the remedies are different. A typo is fixed in your code. A disabled namespace is fixed by changing endpoint, changing node type, or changing which method you call. Treating -32601 as a code bug when it is a capability statement leads to wasted debugging time.
- Unknown name: typo, wrong casing, wrong chain dialect, client-fork-only method.
- Unavailable method: namespace disabled, non-archive node, provider plan gating.
- The error payload alone often cannot tell you which cause applies — probe to find out.
A Capability Probe That Runs at Application Startup
Because there is no standardized capability-discovery call in JSON-RPC, the reliable approach is to probe. A probe is a small set of cheap calls, one per namespace your application depends on, executed at startup. First prove connectivity with a low-cost method such as eth_chainId or net_version. Then attempt one representative method from each namespace you need and record the outcome.
The example below uses Node.js with the built-in fetch API, so it has no dependencies. It calls eth_chainId to confirm the endpoint is reachable, then probes eth, net, web3, debug, trace, txpool, and admin with one method each. It records the code and message for every attempt and prints a summary. Run it against each endpoint you are considering and compare the output.
Keep the probe list small and cheap. Do not probe with expensive methods such as debug_traceTransaction on a production endpoint at high frequency. A single call per namespace at startup is enough to learn whether the namespace is present.
// capability-probe.js — Node.js 18+ (built-in fetch, no dependencies)
const ENDPOINT = process.env.RPC_URL || "https://your-endpoint.example";
// One cheap, representative method per namespace.
const PROBES = [
{ ns: "eth", method: "eth_chainId", params: [] },
{ ns: "net", method: "net_version", params: [] },
{ ns: "web3", method: "web3_clientVersion", params: [] },
{ ns: "debug", method: "debug_traceTransaction", params: ["0x" + "00".repeat(32)] },
{ ns: "trace", method: "trace_block", params: ["latest"] },
{ ns: "txpool", method: "txpool_status", params: [] },
{ ns: "admin", method: "admin_peers", params: [] }
];
async function call(method, params) {
const body = { jsonrpc: "2.0", id: 1, method, params };
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body)
});
const json = await res.json();
return { http: res.status, json };
}
(async () => {
// Step 1: prove connectivity with a low-cost call.
const ping = await call("eth_chainId", []);
if (ping.json.error) {
console.error("Endpoint unreachable or rejecting requests:", ping.json.error);
process.exit(1);
}
console.log("Connected. chainId =", ping.json.result);
// Step 2: probe each namespace and record the outcome.
const rows = [];
for (const p of PROBES) {
try {
const { http, json } = await call(p.method, p.params);
const err = json.error;
rows.push({
namespace: p.ns,
method: p.method,
http,
code: err ? err.code : "ok",
message: err ? err.message : "success",
enabled: err ? "n" : "y"
});
} catch (e) {
rows.push({
namespace: p.ns,
method: p.method,
http: "network-error",
code: "-",
message: String(e),
enabled: "?"
});
}
}
console.table(rows);
const missing = rows.filter(r => r.code === -32601).map(r => r.namespace);
if (missing.length) {
console.warn("Namespaces returning -32601:", missing.join(", "));
}
})();Static Capability Metadata Versus Probing
Some providers publish static capability metadata: a documentation table, a capabilities endpoint, or a machine-readable manifest listing supported namespaces per chain. When it exists, it is a useful starting point. But exposure changes without notice. A provider may enable a namespace, disable it during an incident, or move it behind a plan tier. A docs table that was accurate last quarter may be wrong today.
Probing is more reliable because it measures the endpoint you are actually using, at the moment you use it. The tradeoff is that a probe list must be maintained by hand as chains add namespaces. There is no standardized capability-discovery call in JSON-RPC, so your probe list is a living artifact. Treat static metadata as documentation and probing as verification.
A practical pattern is to combine both: read the provider's documented capabilities to build your initial probe list, then run the probe at startup and log the result. If the probe disagrees with the documentation, trust the probe and file the discrepancy with the provider.
- Static metadata: fast to read, but can be stale or plan-specific.
- Probing: authoritative for the endpoint in front of you, but requires maintenance.
- Combine them: document to plan, probe to verify.
Namespace Availability, Node Type, and Archive Requirements
Namespace availability is not the only gate. Node type matters too. Historical state methods, such as eth_getBalance at an old block or debug_traceTransaction over historical blocks, require an archive node even when the namespace is enabled. A full node prunes old state, so the method may exist and the namespace may be on, yet the call fails because the requested block is no longer available.
This produces a different error than -32601 in many cases, often a missing-trie-node or state-not-available message. But the practical lesson is the same: a successful probe of a namespace does not guarantee that every method in that namespace will work for every block height. If your application needs historical state, confirm archive availability separately from namespace availability.
For Substrate-based chains, runtime metadata and versioning add another dimension; the Substrate state_getMetadata and runtime versions article covers how metadata calls behave across runtime upgrades. The principle generalizes: capability has layers — namespace, node type, and chain state.
- Namespace enabled + full node = historical state methods may still fail.
- Namespace enabled + archive node = historical state methods are more likely to succeed.
- Probe the namespace, then verify archive behavior with a historical block call.
Transport Constraints: HTTP Versus WebSocket Subscriptions
Transport is another capability layer. Subscription methods such as eth_subscribe and eth_unsubscribe are WebSocket-only in common Ethereum JSON-RPC implementations. If you send eth_subscribe over HTTP, the endpoint may return -32601 even though the eth namespace is fully enabled. The method is not unavailable because the namespace is off; it is unavailable over that transport.
This is a frequent source of confusion because the same endpoint may serve both HTTP and WebSocket on different URLs. If your probe runs over HTTP and reports eth as enabled, that does not tell you whether subscriptions work. Probe subscriptions over the WebSocket transport specifically, or check the provider's documentation for the subscription URL.
The general rule: a capability probe must use the same transport your application will use. An HTTP probe validates HTTP methods. A WebSocket probe validates subscriptions. Do not infer one from the other.
- eth_subscribe and eth_unsubscribe are commonly WebSocket-only.
- HTTP may return -32601 for a subscription method even when eth is enabled.
- Probe over the transport you will actually use in production.
Per-Namespace Results Table for Your Own Endpoints
Use the table below to record what each of your endpoints actually supports. Fill it in by running the probe from the earlier section against each endpoint and copying the code and message into the table. Keep one table per environment (development, staging, production) because exposure can differ by plan and region.
The "enabled" column is your conclusion, not the raw code. A -32601 means not enabled for that method over that transport. A success means enabled. A network error means unknown — retry before concluding. The note column is where you record context such as "archive required" or "WebSocket only".
- Namespace | Method | Code | Message | Enabled (y/n) | Note
- eth | eth_chainId | | | |
- net | net_version | | | |
- web3 | web3_clientVersion | | | |
- debug | debug_traceTransaction | | | | archive required for historical blocks
- trace | trace_block | | | |
- txpool | txpool_status | | | |
- admin | admin_peers | | | |
- eth (ws) | eth_subscribe | | | | WebSocket transport only
Decision Guide: Change Method, Change Endpoint, or Change Node Type
Once you have probe results, the decision is usually straightforward. If the method name is wrong — a typo, wrong casing, or a method from another chain's dialect — change the method. Verify the exact name against the Ethereum JSON-RPC specification or the relevant chain's documentation before assuming the endpoint is at fault.
If the method name is correct and the namespace returns -32601, change the endpoint. Choose a provider or plan that documents the namespace you need. For Ethereum mainnet endpoints, the networks/eth page lists available networks, and RPC pricing describes how plan tiers relate to namespace access. If you are building a service that needs guaranteed namespace access, the API service page describes managed access options.
If the namespace is enabled but historical calls fail, change the node type to an archive node. If subscriptions fail over HTTP, change the transport to WebSocket. Each of these is a different fix for a different layer of the capability stack.
- Wrong method name → fix the method in your code.
- Correct name, -32601 → change endpoint or plan.
- Namespace on, historical call fails → change to archive node.
- Subscription fails over HTTP → change transport to WebSocket.
Troubleshooting Checklist for -32601
Work through this checklist in order. It moves from the cheapest checks to the most expensive, so you avoid changing infrastructure for a problem that a one-character fix would solve.
If none of these resolve the error, the endpoint genuinely does not expose the namespace. At that point the decision guide above applies. For a broader walkthrough of endpoint selection and common integration issues, the OnFinality Learn hub collects related guides, and the Migrating deprecated Solana RPC methods article shows how a specific method removal plays out in practice.
- Compare the method name character by character against the specification, including casing.
- Confirm you are not calling a subscription method over HTTP.
- Check whether a proxy, load balancer, or gateway is stripping or rewriting the request path.
- Retry once to rule out a transient routing 404 that surfaced as a JSON-RPC error.
- Verify the endpoint URL points to the chain you intend, not a testnet or a different network.
- Run the capability probe and record the exact code and message.
- If the namespace is enabled, test a historical block to check archive availability.
Limitations and Tradeoffs of Capability Discovery
There is no standardized capability-discovery call in JSON-RPC. The specification defines error codes and method semantics but does not define a method that lists supported methods. This means every capability probe is a hand-maintained list. As chains add namespaces — for example, new L2-specific namespaces or client-fork methods — your probe list must be updated to cover them.
A provider may also return -32601 for a method it intends to add later. The method is not available today, but the absence is a roadmap state rather than a permanent limitation. Probing tells you the current truth; it does not tell you the provider's plans. For planning, combine probe results with the provider's published roadmap or support channel.
Finally, probing adds startup latency and a small number of requests. For most applications this is negligible, but for latency-sensitive services you may want to cache probe results and refresh them periodically rather than on every process start. The tradeoff is between freshness and startup cost.
- No standardized capability-discovery call exists in JSON-RPC.
- Probe lists must be maintained by hand as chains add namespaces.
- -32601 may indicate a method the provider plans to add later.
- Cache probe results if startup latency matters.
Next Steps for Reliable Namespace Integration
Start by running the capability probe against every endpoint in your environment and filling in the results table. That single exercise converts guesswork into a documented capability map. Then encode the results into your application's startup checks so a missing namespace fails fast with a clear message instead of surfacing as a mysterious -32601 deep in a request handler.
If your workload depends on trace, debug, or txpool, review the dedicated articles on the Ethereum trace and debug namespaces and the Ethereum txpool namespace to understand the cost and data characteristics of those methods. If you are still choosing an endpoint, the RPC endpoints guide (RPC Assistant) and RPC pricing pages help you match plan tiers to namespace requirements.
The durable habit is simple: never assume a namespace is available. Probe it, record it, and re-probe when you change endpoints or plans. That habit turns -32601 from a confusing failure into a clear, actionable signal about endpoint capability.
- Run the probe against every endpoint and record results.
- Add startup checks that fail fast on missing namespaces.
- Re-probe after any endpoint or plan change.
- Use the OnFinality Learn hub for related integration guides.