fix(mobile): polyfill the AbortSignal gaps the connection depends on - #310
Merged
Conversation
React Native ships a 2019 AbortController without `reason` or `throwIfAborted`, so every retry given a signal threw before dialing and the client never reached the network (CODE-462). Surfaces the controller's error on the connection screen too: the cause was there all along, thrown away one layer above.
Master's lint script OOMs at --concurrency=2 and its pi closure manifest test fails; both arrived with this merge and neither is reachable from apps/mobile. Verified with --concurrency=auto: 0 errors.
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.
Fixes CODE-462. The mobile client never reached the network at all — the failure was a
TypeErrorthrown before the first dial, not anything about reachability.Root cause
React Native installs
AbortController/AbortSignalfrom theabort-controllernpm package (react-native/Libraries/Core/setUpXHR.js), a 2019 polyfill predating two parts of the spec this codebase uses:signal.reasonandsignal.throwIfAborted().foxts/async-retryopens withoptions.signal?.throwIfAborted()— the optional chain guards the signal, not the method.ConnectionControllerpasses a signal, so every recovery attempt threwTypeError: undefined is not a functionon entry. No HTTP request ever left the app, which is why the symptom was identical from127.0.0.1,localhost, and a LAN IP, and why the daemon logged nothing.Verified in the runtime rather than inferred:
signal.throwIfAbortedundefinedsignal.reasonafterabort(new Error(...))undefinedAbortSignal.timeout/.anyreasonis patched alongside, because the two are one feature:throwIfAbortedis specified to throwreason, andConnectionControllerre-throwssignal.reasonwhen a run is superseded — unpatched that throwsundefined, which nocatchcan classify.Also here
The connection screen now shows why it failed.
ConnectionSnapshotcarried the error all along anduseHostClientdropped it, so both the user and anyone triaging saw only "unable to reach". That gap is what made this bug take a full session to find. It immediately earns its place — the three states are now distinguishable at a glance:xhr poll errorLinkCodeClient: handshake timed out after 5000ms; daemon unavailable or wire protocol mismatchA crash in the new-thread sheet, shipped in #306 and undetectable until a connection existed:
BottomSheetneeds a<Host>like every other@expo/uiview, and red-boxed without one the moment "New thread" was tapped. Its source reads like a plain RN view, which is what I got wrong;apps/mobile/AGENTS.mdis corrected. Also givenfitToContents, without which SwiftUI presents this short sheet at a near-full-screen detent.Verification
pnpm check:ci,pnpm test(2043 passed), 3/3 Maestro flows.The three screens #306 could not verify are now verified against a live daemon: the thread inbox (host subtitle, empty state), the new-thread sheet (agent picker, workspace picker populated from the host), and the terminal list.