expo / dev-plugins

Out-of-tree Expo devtools plugins for packages that haven't adopted the Expo devtools plugin system yet.
MIT License
154 stars 24 forks source link

App crashes on splash screen #19

Open alioguzhan opened 9 months ago

alioguzhan commented 9 months ago

Thanks for the great work for Expo 50.

I enabled the DevTools plugin for react-navigation in my index.tsx file:

image

The app works fine with the dev server locally. But When I build it and try the development build (apk file on my device or emulator), the app hangs at the Splash screen. And this is the adb log from the console:

01-20 14:00:03.693  4767  4812 I cr_LibraryLoader: Successfully loaded native library
01-20 14:00:03.693  4767  4812 I cr_CachingUmaRecorder: Flushed 8 samples from 8 histograms.
01-20 14:00:03.699  4767  4811 E ReactNativeJS: Error: Failed to setup client from useDevToolsPluginClient: [object Object]
01-20 14:00:03.699  4767  4811 E ReactNativeJS:
01-20 14:00:03.699  4767  4811 E ReactNativeJS: This error is located at:
01-20 14:00:03.699  4767  4811 E ReactNativeJS:     in Index
01-20 14:00:03.699  4767  4811 E ReactNativeJS:     in RCTView
01-20 14:00:03.699  4767  4811 E ReactNativeJS:     in Unknown
01-20 14:00:03.699  4767  4811 E ReactNativeJS:     in RCTView
01-20 14:00:03.699  4767  4811 E ReactNativeJS:     in Unknown
01-20 14:00:03.699  4767  4811 E ReactNativeJS:     in AppContainer, js engine: hermes
01-20 14:00:03.701   402   402 I RanchuHwc: validateDisplay: layer 66 CompositionType 1, fallback to client
01-20 14:00:03.701   402   402 I RanchuHwc: validateDisplay: layer 65 CompositionType 1, fallback to client
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: Error: Failed to setup client from useDevToolsPluginClient: [object Object]
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: This error is located at:
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:     in Index
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:     in RCTView
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:     in Unknown
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:     in RCTView
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:     in Unknown
01-20 14:00:03.702  4767  4812 E unknown:ReactNative:     in AppContainer, js engine: hermes, stack:
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: ?anon_0_@1:3334106
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: asyncGeneratorStep@1:148149
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: _throw@1:148451
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: tryCallOne@53:15
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: anonymous@139:26
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: anonymous@1:321582
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: _callTimer@1:320581
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: _callReactNativeMicrotasksPass@1:320725
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: callReactNativeMicrotasks@1:322659
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: __callReactNativeMicrotasks@1:163417
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: anonymous@1:162552
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: __guard@1:163291
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: flushedQueue@1:162463
01-20 14:00:03.702  4767  4812 E unknown:ReactNative: callFunctionReturnFlushedQueue@1:162319

Versions

expo: 50.0.2 react-navigation/native: 6.1.9 react-navigation/stack: 6.3.20 react-navigation/drawer: 6.6.6

Not sure if this is related to this plugin or react-router. Thanks.

Kudo commented 9 months ago

sorry i was not doing good for the error message, could you help to troubleshoot at your node_modules/expo/build/devtools/index.js to dump the error and its stack

        async function setup() {
            try {
                const client = await getDevToolsPluginClientAsync(pluginName);
                setClient(client);
            }
            catch (e) {
                setError(new Error('Failed to setup client from useDevToolsPluginClient: ' + e.toString()));
            }
        }

in the catch section, trying to console.log(e.stack) or console.log(e)

alioguzhan commented 9 months ago

Will do it when I get home @Kudo,

In addition to that, Should we replace the e.toString() with JSON.stringify(e) ? I can create a PR for that. toString fails when the error is an object. JSON.stringify can parse and stringify strings, numbers, objects, and arrays.

Kudo commented 9 months ago

thanks. welcome for the contribution. let's see what's the error first. my thought was only to decorate the error. maybe we could do something like that:

      try {
        const client = await getDevToolsPluginClientAsync(pluginName);
        setClient(client);
      } catch (e) {
        e.message = 'Failed to setup client from useDevToolsPluginClient: ' + e.message;
        setError(e);
      }
jthoward64 commented 9 months ago

Will do it when I get home @Kudo,

In addition to that, Should we replace the e.toString() with JSON.stringify(e) ? I can create a PR for that. toString fails when the error is an object. JSON.stringify can parse and stringify strings, numbers, objects, and arrays.

My goto for errors is usually:

String(error)

as JSON.stringify can throw depending on the object it gets, calling String() has the same functionality as .toString() or Error.prototype.message but handles any value safely.

Kudo commented 9 months ago

the downside of new Error(...) is that we will miss the original stack trace. since we will rethrow the exception later, it would be good if we can find a way to keep the original stack.

jthoward64 commented 9 months ago

Well, there is Error.prototype.cause but IDK how much support there is for that

Kudo commented 9 months ago

that's why i'd tried for e.message = 'Failed to setup client from useDevToolsPluginClient: ' + e.message; and not to re-create a new Error instance.

alioguzhan commented 9 months ago

I am back.

But I couldn't get the error message content. Editing the file under node_modules is not working because both expo run and eas build and even eas build --local are not using my modified node_modules folder. They all run npm install internally.

And this error doesn't exist in the local development environment (npx expo start). So I think 2 options here:

1 - I will fork this repository, edit that file, and add my fork as a dependency instead of this one. 2 - We will replace that toString() part with String(e) and release a new version of devtools plugin (if possible)

What do you suggest ?

alioguzhan commented 9 months ago

Will do it when I get home @Kudo, In addition to that, Should we replace the e.toString() with JSON.stringify(e) ? I can create a PR for that. toString fails when the error is an object. JSON.stringify can parse and stringify strings, numbers, objects, and arrays.

My goto for errors is usually:

String(error)

as JSON.stringify can throw depending on the object it gets, calling String() has the same functionality as .toString() or Error.prototype.message but handles any value safely.

I didn't know that JSON.stringify could throw depending on the object. And looks like you are right. This is from MDN:

Thrown in one of the following cases:

  • value contains a circular reference.
  • A BigInt value is encountered.

But String() does not throw for any value. Thanks for the tip @jthoward64

Kudo commented 9 months ago

But I couldn't get the error message content. Editing the file under node_modules is not working because both expo run and eas build and even eas build --local are not using my modified node_modules folder. They all run npm install internally.

good to know it's using eas build. in this case you may need to modify and use patch-package to generate the patch and on postinstall to apply the patch.

alioguzhan commented 9 months ago

I think this is the error message:

{ isTrusted: false, message: [Getter/Setter] }

But I am not sure 100%. Does this make any sense in this context ? If not I can keep digging. @Kudo

Kudo commented 9 months ago

looks strange to me, having no idea how it comes from. could you try to dump to stack trace?

alioguzhan commented 9 months ago

Sorry for the limited feedback. It is hard to work with update -> patch -> build -> install to emulator -> adb logs flow :) I will update here If I find more.

Kudo commented 9 months ago

no worries! after you generate builds from eas build, you may try to modify node_modules/expo locally before running npx expo start, then you don't need to re-resubmit eas build again.

alioguzhan commented 9 months ago

OK. I was not able to reproduce this on compiled apps. Once a new version is released that fixes that error logging, I will try that again.

I will create a separate issue for that. (or maybe a PR ?)

Btw, if the error is something like { isTrusted: false, message: [Getter/Setter] }, then using String(e) also returns [object Object]. The only method that outputs a proper stringified version was JSON.stringify(e) for me. @Kudo @jthoward64

jthoward64 commented 9 months ago

The only method that outputs a proper stringified version was JSON.stringify(e) for me. @Kudo @jthoward64

You just have to be careful using JSON.stringify because if it throws you will mask the initial error completely. What I usually do (more complex) is use a try catch to prefer JSON.stringify and fallback to String

alioguzhan commented 9 months ago

Yes, that's also what I thought. try/catch for JSON stringify and String makes sense. That's also what I used for encoding/decoding scalars for GraphQL.

DarlonHenrique commented 4 months ago

Im have the same error but only in my production build, when i build the same commit to development it runs ok

bpeltonc commented 4 months ago

I also don't get this in development, but release builds hang after the splash screen hides -- looks to be during the initial render. I was able to log the error with JSON.stringify(e), but the stack trace isn't super helpful.

Error: Failed to setup client from useDevToolsPluginClient: {"isTrusted":false,"message":"The operation couldn’t be completed. Connection refused"}

This error is located at:
    in AppNavigator
    in RCTView
    in Unknown
    in TouchListener
    in RNCSafeAreaProvider
    in SafeAreaProvider
    in App
    in ReactNativeProfiler
    in RCTView
    in Unknown
    in __Sentry.TouchEventBoundary
    in RootApp
    in RCTView
    in Unknown
    in RCTView
    in Unknown
    in AppContainer, js engine: hermes
bpeltonc commented 4 months ago

I'm using this patch as a workaround. I'm not sure that throwing from the hook is the best way to handle errors in this case. Obviously, we should understand what is the root cause of the error and fix that, but I'm thinking there also might be a better way to handle the errors, since this implementation breaks the app entirely.

diff --git a/node_modules/expo/build/devtools/index.js b/node_modules/expo/build/devtools/index.js
index 25c7e7c..560fdfd 100644
--- a/node_modules/expo/build/devtools/index.js
+++ b/node_modules/expo/build/devtools/index.js
@@ -33,7 +33,8 @@ export function useDevToolsPluginClient(pluginName) {
         };
     }, [pluginName]);
     if (error != null) {
-        throw error;
+        // throw error;
+        console.log(`An error was caught in useDevToolsPluginClient: ${JSON.stringify(error)}`);
     }
     return client;
 }
osamaqarem commented 4 months ago

Also seeing a similar issue using drizzle dev tools: https://github.com/drizzle-team/drizzle-studio-expo/issues/1

likaci commented 3 months ago

@osamaqarem thanks for saving my day 👍

similar issue using drizzle dev tools

abdibaker commented 1 month ago

@osamaqarem thanks a lot I had this issue for week now it gone after remove useDrizzleStudio()

tasmto commented 1 month ago

Also seeing a similar issue using drizzle dev tools: drizzle-team/drizzle-studio-expo#1

Thank you 😭

kimuradev commented 3 weeks ago

I had similar issue and I solved using variable EXPO_PUBLIC_DISABLE_FEATURE=true to use drizzle studio only at development env.

 if (!process.env.EXPO_PUBLIC_DISABLE_FEATURE) {
        useDrizzleStudio(expoDb)
    }