HelpExampleRpc interpolates its args string directly into a JSON-RPC payload
(src/rpc/util.cpp:201-204) with no validation that the result is a well-formed
JSON array body. On master (556988790a), 13 of the 138 literal HelpExampleRpc
call sites produce a payload that fails JSON parsing, so the documented curl
command is unusable as printed.
Three failure modes:
Missing comma separators (CLI-style arg string reused in the RPC example):
- src/rpc/blockchain.cpp:543
getblockfrompeer - src/rpc/net.cpp:338
addnode - src/rpc/net.cpp:413
addconnection - src/rpc/net.cpp:1086
sendmsgtopeer - src/wallet/rpc/backup.cpp:628
restorewallet
addnode shows this is an oversight rather than style — it has the first comma
and is missing only the second:
HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\" true")
- HelpExampleRpc("addnode", ""192.168.0.6:8333", "onetry" true")
renders "params": ["192.168.0.6:8333", "onetry" true]
→ Expecting ',' delimiter: line 1 column 99
Bare unquoted strings (valid as a CLI placeholder, a syntax error in JSON):
src/rpc/mempool.cpp:871 getmempoolcluster, src/rpc/mempool.cpp:1171
importmempool, src/rpc/node.cpp:381 getindexinfo,
src/wallet/rpc/addresses.cpp:646 listlabels, src/wallet/rpc/wallet.cpp:454
unloadwallet, src/wallet/rpc/wallet.cpp:774 createwalletdescriptor,
src/wallet/rpc/wallet.cpp:864 addhdkey.
Invalid backslash escape — src/wallet/rpc/wallet.cpp:246 loadwallet,
the "absolute path (Windows)" example renders
"params": ["DriveLetter:\path\to\walletname\"] → Invalid \escape. The
trailing \" also escapes the closing quote. The sibling CLI example on line
245 is correct as-is; only the JSON form needs the backslashes doubled.
Why file this now rather than another round of manual fixes
In #31275 sipa ran the runtime version of this check ("I wanted to verify if all
invocations of HelpExampleRpc produce valid JSON, so I tested by adding a
UniValue::read inside the function", 2025-11-10) and listed 11 failing RPCs.
That list is an exact subset of the 13 above. Since then:
| RPC | landed on master | vs. the 2025-11-10 audit |
|---|---|---|
getmempoolcluster |
2025-11-25 (fa283d28e2, #33629) |
+15 days |
addhdkey |
2026-05-14 (cad5f56045, #29136) |
+6 months |
None of the original 11 were fixed, two more were added, and #31275 was closed
unmerged on 2026-03-14 — addhdkey's example landed two months after that. Six
separate PRs have fixed instances of this one-by-one over the years and the
population still grows, because nothing enforces it.
Proposed direction
- Add the guard first.
test/functional/rpc_help.py::dump_help()already iterates every registered command and callsself.nodes[0].help(call). Extend it to extract each> curl ... --data-binary '<payload>'line from the help text and assertjson.loads(payload)succeeds. Roughly ten lines, no new test file, covers every current and future RPC, and would have caught all 13 — including the ~16 call sites whose args are built from expressions (EXAMPLE_ADDRESS,strprintf, …) that a static scan has to skip. This is sipa's second suggestion from #31275. - Then fix the 13: add the missing commas, quote the bare placeholders,
and double the backslashes in the
loadwalletWindows example.
Worth settling direction before anyone writes a patch: sipa's first suggestion was to drop the curl examples entirely, which would moot the fixes. Happy to open a PR for whichever the maintainers prefer.