Skip to content

Latest Detox version disconnects from the app, invoke command isReady failed #4824

@Glisa060

Description

@Glisa060

Did you test using the latest Detox?

  • I have tested this issue on the latest Detox release and it still reproduces.

Did your test fail and you're not sure why?

  • I have read the troubleshooting guide and it didn't help me.

What happened?

I upgraded RN to "0.78.2" after I upgraded Detox to the latest version.

iPhone 16 simulator running on MacBook Air m1, 2020 Sequoia 15.6.1
XCode 16.2

Our nightly regression on CI/CD stopped working. It is reproducible locally.

App disconnects from detox with this error?

Image Image

My launch code prints errors it encounters.

I tried many things to mitigate this issue. Including disabling synchronization and enabling it back, tried different permissions, nothing helps.

Sometimes after a second try it launches the app, but sign in fails immediately sometimes a test passes, it's very hard to isolate. It fails in the "Before All" block and just goes to sign out that is located in the after all block? Which of course fails since we are not signed in to begin with.

Screenshot where tests fail before login is attempted.

Image

With verbose logging

Image

What was the expected behavior?

Detox should sign in and start executing test cases.

Help us reproduce this issue!

This version was working fine"20.27.6"
This is iOS specific, Android is working fine.
We upgraded because we anticipated RN upgrade is coming. To make sure we have support for the new version.

My launch code:

xport const launchApp = async (
  newInstance = true,
  permissionsList?: Detox.DevicePermissions | undefined,
  reinstall?: boolean | undefined,
  retryOnFail = true,
) => {
  const maxRetries = retryOnFail ? 3 : 1;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      console.log(`🚀 Launching app... (Attempt ${attempt}/${maxRetries})`);

      // On the first attempt, respect the 'reinstall' flag. On all subsequent retries,
      // force a reinstall to ensure a clean state, which often solves launch issues.
      if (reinstall || attempt > 1) {
        console.log('Ensuring clean state by reinstalling the app...');
        try {
          await device.terminateApp();
          await sleep(500); // Give it time to fully terminate
        } catch (e) {
          // App might not be running, that's ok
        }

        await device.uninstallApp();
        await device.installApp();
      }

      // Launch the app with merged permissions
      await device.launchApp({
        newInstance,
        permissions: {
          microphone: 'YES',
          notifications: 'YES',
          contacts: 'YES',
          ...permissionsList,
        },
        launchArgs: {detoxURLBlacklistRegex: ignoreDetoxApi()},
      });

      // Perform platform-specific setup after launch
      if (device.getPlatform() === 'android') {
        await disableHeadsUpNotificationsOnAndroid();
      }

      // Wait for the app to be in a known "ready" state
      await waitForAppReady();

      console.log('✅ App launched successfully and is ready!');
      return; // Success! Exit the function.
    } catch (err: any) {
      console.error(`❌ Launch attempt ${attempt} failed: ${err.message}`);
      await device.takeScreenshot(`launch-failure-attempt-${attempt}`);

      if (attempt === maxRetries) {
        console.error('All launch attempts have failed.', err);
        // Re-throw the last error to fail the test suite
        throw new Error(
          `Failed to launch app after ${maxRetries} attempts. Last error: ${err.message}`,
        );
      }

      console.log('Waiting 3 seconds before the next attempt...');
      await new Promise((resolve) => setTimeout(resolve, 3000));
    }
  }
};

const waitForAppReady = async (timeout = 25000) => {
  console.log('⏳ Waiting for app to become ready...');

  const loginScreenElement = element(by.text('Sign in'));

  try {
    // Promise.race will succeed as soon as ONE of the elements becomes visible.
    // This handles both logged-out and logged-in states gracefully.
    await Promise.race([
      waitFor(loginScreenElement).toBeVisible().withTimeout(timeout),
    ]);

    console.log('✅ App is ready.');
  } catch (e) {
    // If neither element appeared, the app is not in a ready state.
    throw new Error(
      `App failed to become ready. Neither the login screen nor the main dashboard was visible within ${timeout}ms.`,
    );
  }
};

We didn't change anything after upgrade. Tests are the same as before?
Any idea how to debug our issue?

In what environment did this happen?

Detox version: 20.40.2
React Native version: 0.78.2
Has Fabric (React Native's new rendering system) enabled: (yes)
Node version: 20.18.1
Device model: iPhone 16
iOS version: 18.5
macOS version: 15.6.1
Xcode version: 16.2
Test-runner (select one): jest

Detox logs

Detox logs
paste logs here!

Device logs

Device logs
paste your device.log here!

More data, please!

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions