Skip to content

fix(electrum): return JSON-RPC error for unknown methods instead of dropping the connection#220

Open
EddieHouston wants to merge 1 commit into
Blockstream:new-indexfrom
EddieHouston:fix/electrum-unknown-method-error
Open

fix(electrum): return JSON-RPC error for unknown methods instead of dropping the connection#220
EddieHouston wants to merge 1 commit into
Blockstream:new-indexfrom
EddieHouston:fix/electrum-unknown-method-error

Conversation

@EddieHouston

@EddieHouston EddieHouston commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Resolves #219

Problem

Any method without a dispatch arm in handle_command — a typo, an unimplemented
Electrum method (blockchain.scripthash.get_mempool,
blockchain.transaction.get_tsc_merkle), or blockchain.scripthash.get_balance on
Liquid builds (where the arm is compiled out via #[cfg(not(feature = "liquid"))]) —
causes the server to close the TCP connection without writing any JSON-RPC response.
Clients cannot distinguish an intentionally unsupported method from an outage, and
retrying clients turn one cheap error reply into repeated TCP+TLS reconnects.

Inside a batch request, a single unknown method kills the whole batch and the
connection; the remaining requests are never answered.

Cause

The catch-all arm in handle_command:

&_ => bail!("unknown method {} {:?}", method, params),

bail! returns Err from the whole function, bypassing the error-to-JSON conversion
a few lines below — that conversion only sees errors that dispatched handlers assign
into result. The unknown-method error instead propagates through the ? in
handle_value and handle_replies, and run() logs "connection handling failed"
and shuts the socket down.

Fix

Produce the error as a value inside result, so it flows through the existing
conversion and is returned as a normal JSON-RPC error reply:

&_ => Err(format!("unknown method {}", method).into()),
  • The connection stays open, matching other handler errors in electrs and other
    Electrum server implementations (ElectrumX, Fulcrum).
  • params are deliberately omitted from the message so client-controlled input is
    not echoed back; only the method name is included.
  • Batches now answer their remaining requests when one entry has an unknown method.

Prior art

Both related projects have already fixed this. mempool/electrs (a fork of this
codebase) replaced the catch-all in mempool/electrs#103 (merged 2024-11-14) to return
a JSON-RPC error object with code -32601 and keep the connection open;
romanz/electrs (the v0.9 rewrite) returns {"code": -32601, "message": "method not found"} via its typed RpcError enum. This PR keeps the existing bare-string error
shape for minimal diff; moving to JSON-RPC 2.0 error objects across the board is left
to a follow-up.

Out of scope

Malformed input still drops the connection, unchanged: invalid JSON, requests
without a string method / array params / id, oversized batches, invalid UTF-8,
and TLS bytes on the plaintext port. The first two arguably deserve error replies as
well (with "id": null per JSON-RPC), but that is a larger change than this fix.

Testing

Against a local instance:

echo '{"jsonrpc":"2.0","id":1,"method":"blockchain.scripthash.get_mempool","params":["e573ed3c89a107e4c5b67cebc6a6cadb2faa40fb02a7c5092d9b4d4f118a3efe"]}' | nc localhost 50001

Before: connection closes with no payload.
After: {"error":"unknown method blockchain.scripthash.get_mempool","id":1,"jsonrpc":"2.0"}
and the connection remains open. A batch mixing server.ping with an unknown method
now returns results for both entries instead of dropping the connection.

Full cargo test (lib + integration: 4 electrum, 22 REST) passes.

…ropping the connection

  The catch-all arm in handle_command used bail!, which returns Err from the
  whole function and bypasses the error-to-JSON conversion below it. The error
  then propagated out of handle_replies and run() shut the socket down, so any
  unknown method (including methods compiled out by feature flags, like
  scripthash.get_balance on Liquid builds) closed the connection with no reply,
  and a single unknown method inside a batch killed the entire batch.

  Produce the error as a value inside 'result' instead, so it is returned as a
  normal JSON-RPC error reply and the connection stays open. The method's params
  are no longer echoed back in the error message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Electrum RPC: unknown methods close the connection without a JSON-RPC error reply

1 participant