Stop daemon broadcast events from rejecting pending wallet_ui requests - #3012
Merged
Starttoaster merged 1 commit intoAug 19, 2026
Merged
Conversation
The main-process sendCommand socket registers as service wallet_ui, so the daemon forwards every broadcast event to it (farming_info, get_fee_estimate, get_connections, ...). Its message handler parsed each frame with json-bigint's useNativeBigInt parser before checking request_id, and that parser passes any number literal longer than 15 characters to BigInt() even when it contains a decimal point. A harvester farming_info lookup time such as 1.7656183690123726 therefore threw "Cannot convert 1.7656183690123726 to a BigInt", and the catch block rejected whichever request happened to be pending. Because get_offer_summary runs inside the take_offer confirmation flow, any farm with slow-enough plot lookups hits this race on nearly every offer accept: the user sees the BigInt error dialog and the offer command is never sent to the wallet. Parse incoming frames with the native-bigint parser first, fall back to the BigNumber parser for frames carrying long-precision floats, and ignore frames that are not valid JSON instead of rejecting the pending request. Matched responses keep their existing native-bigint behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jfMfuGTvCmMyL7EM92dmj
Contributor
|
close and reopen to get new CI run with updated packages |
emlowe
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Accepting an offer in the Offers tab can fail with an error dialog like:
The offer is never sent to the wallet (no spend is pushed). On farms with slower plot lookups this happens on nearly every accept attempt.
Root cause
packages/gui/src/electron/api/sendCommand.tsregisters its daemon socket as servicewallet_ui, so the daemon forwards every broadcast event to it (farming_info,get_fee_estimate,get_connections, …) in addition to RPC responses.Its
handleMessageparsed every incoming frame withJSONBig({ useNativeBigInt: true })before checkingrequest_id. json-bigint's parser passes any number literal longer than 15 characters toBigInt()— even when it contains a decimal point ([json-bigintlib/parse.js, thestring.length > 15branch]). A harvesterfarming_infolookup time such as1.7656183690123726therefore throwsSyntaxError: Cannot convert 1.7656183690123726 to a BigInt, and thecatchrejected whichever request happened to be pending.The user-visible failure chain: Offers-tab accept →
take_offerintercepted by the main-process confirmation layer (onSendinmain.tsx) →parseCommandDisplay→getOfferSummary→sendCommand('get_offer_summary')→ an unrelated broadcast with a long-precision float lands during the round trip → the pending request is rejected →onSendthrows → the bridge synthesizes an error response → the renderer shows the BigInt error andtake_offeris never transmitted.Verified against a live mainnet daemon: a 30-second passive
wallet_uilistener observed multiple poison frames (chia_harvester farming_infowithtimefloats of 1.2–1.8 s,chia_full_node get_fee_estimate,chia_farmer get_connectionsfloat timestamps), so the race window is hit almost every time on affected setups.The same flaw exists in
connect()'sregister_servicehandler, where a poison frame during the handshake tears down the socket.Fix
Parse incoming frames with the native-bigint parser first, fall back to the BigNumber-based parser (json-bigint's default, which handles long-precision floats fine) when it throws, and ignore frames that are not valid JSON at all instead of rejecting the pending request. Matched responses keep their existing native-bigint behavior for large integers.
Tests
Four new regression tests in
sendCommand.test.ts(all four fail against the previous implementation):farming_info-style broadcast with a >15-char float while a command is pendingregister_serviceExisting tests, including "parses unsafe integer response values as native bigints", still pass (10/10).
🤖 Generated with Claude Code
https://claude.ai/code/session_016jfMfuGTvCmMyL7EM92dmj
Note
Medium Risk
Changes core Electron daemon IPC parsing for all wallet commands; behavior is intentional but any mismatch in request_id handling or fallback parsing could affect RPC reliability.
Overview
Fixes intermittent offer accept failures (
Cannot convert … to a BigInt) when unrelated daemon broadcast frames arrive while awallet_uicommand is in flight.Incoming WebSocket frames are parsed via a new
parseIncomingMessage: try native-bigintjson-bigintfirst, fall back to the default BigNumber parser when long-precision floats tripBigInt(), and drop non-JSON frames instead of rejecting the pending RPC. The same logic applies duringregister_service.Four regression tests cover poison broadcasts, matched responses with long floats, invalid JSON, and registration-time broadcasts.
Reviewed by Cursor Bugbot for commit 54db007. Bugbot is set up for automated code reviews on this repo. Configure here.