Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Android startup retries do not restore lost adb reverse mappings

Đang mở
#210 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@l2hyunwoo đang làm issue này rồi.

Từ ngày 18/10/2026.

  • #211 của @l2hyunwoo — đang mở

Đánh giá

Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
35/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Đình trệ
Công nghệ
android, react-native, typescript
Lĩnh vực
testing

Hướng nghiên cứu

Read packages/platform-android/src/instance.ts, especially configureAndroidRuntime() and the emulator and physical-device session factories; then compare them with the retry flow in packages/bundler-metro/src/startup.ts. Use the attached reproducer and restore-port-forwarding.patch as evidence, and verify that startup retries restore the configured Metro forwarding and the existing 8080 mapping. A linked open pull request (#211) already addresses this work.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Describe the bug

When the Metro adb reverse mapping disappears between test files, Android app restarts do not restore it.
Metro remains healthy on the host, but the app cannot request its bundle. All three launch attempts fail with the same startup error.

I reproduced this with two test files that each assert 1 + 1 === 2.
Neither test calls a device-info or attestation API.
After the first file passes, a Harness plugin removes only the selected emulator's Metro reverse mapping.
The next file fails before its test executes.

Recorded reproduction

Both recordings use the same emulator, APK, two trivial tests, and deliberate removal of the Metro mapping. The Android runner implementation is the only change. Test-file order varies; both files contain the same assertion.

Before: unpatched 1.6.0. The first file passes. After the mapping is removed, the next file fails after three launch attempts. Exit code 1.

Unpatched run: missing Metro forwarding, three launch attempts, one suite failed

20-second recording (terminal on the left, emulator on the right):

https://github.com/user-attachments/assets/2aadb076-074c-41a5-943b-f84503180fe6

After: candidate patch. Harness restores tcp:8081 before the next launch. Both files and both tests pass. Exit code 0.

Patched run: Metro forwarding restored and both test suites pass

13-second recording:

https://github.com/user-attachments/assets/2989da8e-39f0-4fc6-9ab6-9ebf10c4f754

The videos show the live run at normal speed. The terminal displays selected diagnostic lines with shortened paths. Screenshots are frames from these recordings. The window title is cropped out, and the videos end shortly after the results appear. The emulator and terminal were recorded concurrently and placed side by side.
Full logs from these recorded runs: unpatched, patched.

Expected: Before launching or retrying the app, restore the device-side forwarding required to reach Metro.

Actual: Harness restarts the app without recreating the missing mapping:

The app did not request its Metro bundle after 3 launch attempts within 3000ms.
Last Metro status: HTTP 200: packager-status:running. Only prewarm traffic was observed.

The error is shown on two lines here for readability. The attached log contains the original line.
bundleStartTimeout is set to 3000 ms in the reproducer to shorten failed attempts; the first app launch succeeds.

System Info

Test device: Android emulator, API 35, Google APIs, arm64-v8a.
Android Emulator: 37.2.11.0. ADB/platform-tools: 37.0.1-15733141.
The app uses Hermes and the New Architecture.

yarn react-native info output from the example workspace is below. Personal paths are redacted.
The CLI reports Android SDK: Not Found; the emulator and ADB versions above were checked directly from the installed SDK tools.

React Native environment
info Fetching system and libraries information...
System:
  OS: macOS 27.0.1
  CPU: (18) arm64 Apple M5 Max
  Memory: 2.33 GB / 48.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.16.0
    path: <tmp>/xfs-c56faaac/node
  Yarn:
    version: 4.17.0
    path: <tmp>/xfs-c56faaac/yarn
  npm:
    version: 10.9.2
    path: <home>/.nvm/versions/node/v22.16.0/bin/npm
  Watchman:
    version: 2026.10.12.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.17.0
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 27.0
      - iOS 27.1
      - macOS 27.0
      - tvOS 27.0
      - visionOS 27.0
      - watchOS 27.0
  Android SDK: Not Found
IDEs:
  Android Studio: 2026.2 AI-262.9437.185.2621.16467767
  Xcode:
    version: 27.1/27A9275
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 24.0.2
    path: /usr/bin/javac
  Ruby:
    version: 4.0.7
    path: /opt/homebrew/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 20.2.0
    wanted: 20.2.0
  react:
    installed: 19.3.0
    wanted: 19.3.0
  react-native:
    installed: 0.88.0
    wanted: 0.88.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: false

React Native Harness Version

react-native-harness and @react-native-harness/platform-android: 1.6.0.

The runtime reproduction uses unpatched 1.6.0 Android runner code.
I also reviewed upstream main at 9c257af3ed5f41341235cf44bb16b6c43220395d.
Its platform-android/src/instance.ts is identical to the v1.6.0 file. I did not run the main branch.
I have not established which version first introduced this behavior.

Reproduction

Example repository at the reproduction commit.

This is an existing example app, not a standalone minimal repository.
The attachments supply two trivial test files and a launcher that selects only those files.
The repository commit includes a candidate patch, so the steps below first replace the Android runner with unpatched 1.6.0.

Steps to reproduce

Use Node 22, Yarn 4, and a configured Android build environment. Start an API 35 arm64 emulator.
Replace emulator-5554 below with its serial from adb devices.

  1. Check out the example and select the unpatched Android runner:

    git clone https://github.com/l2hyunwoo/react-native-nitro-device-info.git
    cd react-native-nitro-device-info
    git checkout --detach e6ab56ad9bec5d47d2285f2c5e9a0db9c2f1b11f
    yarn install --immutable
    yarn workspace react-native-nitro-device-integrity-demo add --dev @react-native-harness/[email protected]
    
  2. Build and install the debug example:

    yarn workspace react-native-nitro-device-integrity prepare
    (cd example/integrity-demo/android && ./gradlew :app:assembleDebug -PreactNativeArchitectures=arm64-v8a)
    adb -s emulator-5554 install -r example/integrity-demo/android/app/build/outputs/apk/debug/app-debug.apk
    
  3. Download the three reproduction files individually from the attachments, then run the launcher. The launcher is attached as .mjs.txt because GitHub rejects .mjs uploads; the command saves it with its original extension:

    mkdir -p evidence
    curl --fail --location 'https://github.com/user-attachments/files/33629490/reproduce-reverse-loss.mjs.txt' -o evidence/reproduce-reverse-loss.mjs
    curl --fail --location 'https://github.com/user-attachments/files/33629485/reverse-repro-a.harness.ts' -o evidence/reverse-repro-a.harness.ts
    curl --fail --location 'https://github.com/user-attachments/files/33629486/reverse-repro-b.harness.ts' -o evidence/reverse-repro-b.harness.ts
    cp evidence/reverse-repro-*.harness.ts example/integrity-demo/src/__tests__/
    HARNESS_DEBUG=1 node evidence/reproduce-reverse-loss.mjs integrity-demo emulator-5554
    

The launcher uses resetEnvironmentBetweenTestFiles: true, selects the named emulator, and leaves maxAppRestarts at its default of 2.
After the first passing file, it removes the actual Metro port reported by the metro.initialized hook:

adb('reverse', '--remove', `tcp:${metroPort}`);

It does not kill the shared ADB server. The other emulator mappings remain in place.

Evidence
Supporting logs, reproduction files, and candidate patch

Individual attachments:

Before removal, adb reverse --list contains tcp:8081 tcp:8081 and tcp:8080 tcp:8080.
After removal, only the 8080 mapping remains.
The unpatched trace then shows three app launch attempts, but no new reverse tcp:8081 tcp:8081 command.

Controlled run Result
Unpatched 1.6.0, two trivial files 1 file passed; 1 failed before executing its test
Candidate patch, same two files and mapping removal 2 files passed; 2 tests passed
Candidate patch, integrity-demo full suite 3/3 runs passed; 16 tests per run
Candidate patch, showcase full suite 3/3 runs passed; 109 passed and 6 skipped per run

Each full-suite run also removes the Metro mapping after its first passing file, with process resets enabled.
The full-suite regression uses the normal timeout settings. Both Metro configs omit the deprecated withRnHarness wrapper.

The attachments include complete sanitized before/after logs, the runnable reproducer, the test files, and the candidate patch.
In the patched trace, Harness restores tcp:8081 before the second launch, and the runtime connects successfully.

Suspected cause and candidate fix

configureAndroidRuntime() configures reverse mappings during platform initialization.
The emulator session factory does not repeat that setup when creating subsequent app sessions.
The physical-device factory has the same structure, but I have only tested the emulator.

The startup loop retries after a missing bundle request.
Those retries create another app session without repairing the missing mapping.

The candidate patch extracts the existing reverse setup into a helper and calls it before every app-session launch.
It restores the configured Metro port and preserves the existing 8080 forwarding behavior.
This covers process resets and startup retries without increasing timeouts or disabling test isolation.

To validate the candidate in the reproduction checkout, restore its original patched dependency and rerun:

git restore example/integrity-demo/package.json yarn.lock
yarn install --immutable
HARNESS_DEBUG=1 node evidence/reproduce-reverse-loss.mjs integrity-demo emulator-5554
Scope

An external ADB server shutdown was also observed during the investigation, interrupting an am force-stop command with exit code 255.
That is separate from this controlled reproduction, which keeps ADB running and removes one mapping.
The proposed fix handles missing forwarding on the next launch; it does not make commands immune to an ADB shutdown in progress.
I cannot attribute every earlier intermittent startup failure to this mechanism.

Ngôn ngữ chính
TypeScript
Star
333
Fork
20
Merge trung bình
1 ngày 15 giờ
Pull request đã merge (30 ngày)
5

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của callstackincubator/react-native-harness

Tất cả issue của callstackincubator/react-native-harness

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.