Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symbolicate unhandled promise rejections #40914

Closed
wants to merge 12 commits into from
Closed

Symbolicate unhandled promise rejections #40914

wants to merge 12 commits into from

Conversation

ospfranco
Copy link
Contributor

@ospfranco ospfranco commented Oct 13, 2023

Summary:

For a very long time when a promise rejects without an attached catch we get this warning screen without a correct stack trace, only some internal calls to the RN internals.

I created an issue for discussion in the react-native-community repo and we figured out it was only a matter of symbolication. While it cannot be done on release without external packages and source maps, at least while developing we can provide a symbolicated stack-trace so developers can better debug the source of rejected promise.

I got the stack trace symbolicated and the correct code frame. I'm missing some help trying to display it in the warning view but at the very least I can now correctly show the line of the error and log the codeframe to the console.

Changelog:

Test Plan:

I simply created a throwing function on a dummy app, and checked the output of the console and the warning view:

import React from 'react';
import {SafeAreaView, Text} from 'react-native';

async function throwme() {
  throw new Error('UNHANDLED');
}

function App(): JSX.Element {
  throwme();

  return (
    <SafeAreaView>
      <Text>Throw test</Text>
    </SafeAreaView>
  );
}

export default App;

Here is the output

Edit: I got the warning window working properly:

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 13, 2023
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Oct 13, 2023
@github-actions
Copy link

github-actions bot commented Oct 13, 2023

Warnings
⚠️ One hour and a half have passed and the E2E jobs haven't finished yet.

Generated by 🚫 dangerJS against e5eed45

message: {content: warning, substitutions: []},
isComponentError: false,
codeFrame: stack.codeFrame,
stack: error.stack,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just pass the symbolicated stack variable here instead of the original obfuscated error.stack?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logbox already supports doing the symbolication, can we leverage the logic there?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide more context on how to do that? I can see that he already provided it with the error.stack but still the correct first frame wasn't shown

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may need to use LogBox.addException for this, and update parseLogBoxException to treat these as non-fatal

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ospfranco can you please give this a try?

Copy link
Contributor Author

@ospfranco ospfranco Oct 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, doesn't seem to be working, didn't change the level yet, but stack parsing doesn't seem to work.

@ospfranco
Copy link
Contributor Author

ospfranco commented Oct 13, 2023

I got it working :)

@JuanRdBO
Copy link

Pls merge this now 💪

Copy link
Member

@javache javache left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try getting rid of the symbolication code in promiseRejectionTrackingOptions? We should aim to reuse the existing one.

@@ -198,16 +201,17 @@ export function addLog(log: LogData): void {
// otherwise spammy logs would pause rendering.
setImmediate(() => {
try {
const stack = parseErrorStack(errorForStackTrace?.stack);
const defaultStack = parseErrorStack(errorForStackTrace?.stack);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try this to reuse the same parsing logic for both

Suggested change
const defaultStack = parseErrorStack(errorForStackTrace?.stack);
const stack = parseErrorStack(log.stack ?? errorForStackTrace.stack);

Comment on lines 30 to 36
const parseErrorStack = require('./Core/Devtools/parseErrorStack');
const symbolicateStackTrace = require('./Core/Devtools/symbolicateStackTrace');
const LogBox = require('./LogBox/LogBox').default;

const parsedStack = parseErrorStack(error.stack);

symbolicateStackTrace(parsedStack)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't live here. Logbox can handle this better by first showing the unsymbolicated stacktrace and then replacing the relevant log when the symbolication is ready.

@javache javache requested a review from motiz88 October 13, 2023 13:53
@ospfranco
Copy link
Contributor Author

ospfranco commented Oct 13, 2023

Which of the existing ones? But now that I got it working I now better why it fails. It's more a question of architecture. Maybe add an extra case in AddException? But which logic should I follow to match the error?

@javache
Copy link
Member

javache commented Oct 13, 2023

Which of the existing ones? But now that I got it working I now better why it fails. It's more a question of architecture. Maybe add an extra case in AddException? But which logic should I follow to match the error?

Logbox will auto-symbolicate the current trace given a valid stack property:

@ospfranco
Copy link
Contributor Author

yeah, now I got your point @javache. Thanks a lot!

Comment on lines 36 to 44
const parsedStack = parseErrorStack(error.stack);

LogBox.addLog({
level: 'warn',
message: {content: warning, substitutions: []},
componentStack: [],
stack: parsedStack,
category: 'possible_unhandled_promise_rejection',
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid parsing the error stack here, and leave that op to LogBox?

Consider whether addException may be better here, since it already supports passing in a stack?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried but addException tackes an Array<StackFrame> on the stack property not a string. Tried modifying that but getting another error:

Oscar Franco Screen 000116

So it's a chicken and egg problem trying to use that. I can modify addLog to check for a string stack and parsing it there though, would that be a good compromise?

Copy link
Member

@javache javache left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some final nits, but this seems good for merging in.

@@ -10,6 +10,8 @@

import typeof {enable} from 'promise/setimmediate/rejection-tracking';

const LogBox = require('./LogBox/LogBox').default;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use import instead of require here

Comment on lines 28 to 46
const warning =
`Possible Unhandled Promise Rejection (id: ${id}):\n` +
`${message ?? ''}\n` +
(stack == null ? '' : stack);

// Print pretty unhandled rejections while on DEV
if (__DEV__) {
LogBox.addLog({
level: 'warn',
message: {content: warning, substitutions: []},
componentStack: [],
stack: error.stack,
category: 'possible_unhandled_promise_rejection',
});

return;
} else {
console.warn(warning);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We never write to stack anymore, can you cleanup warning?

And I think this will log twice in !__DEV__ (although this module is only ever used if (__DEV__), so we could remove this check altogether really.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, reverted the double warning and just handled the special case while on dev

facebook-github-bot pushed a commit that referenced this pull request Dec 28, 2023
…info if possible (#42079)

Summary:
This is a continuation of my [last PR](#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
huntie pushed a commit that referenced this pull request Jan 2, 2024
…info if possible (#42079)

Summary:
This is a continuation of my [last PR](#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
Othinn pushed a commit to Othinn/react-native that referenced this pull request Jan 9, 2024
…info if possible (facebook#42079)

Summary:
This is a continuation of my [last PR](facebook#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: facebook#42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
yayvery pushed a commit to discord/react-native that referenced this pull request Jan 10, 2024
…info if possible (facebook#42079)

Summary:
This is a continuation of my [last PR](facebook#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: facebook#42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
yayvery pushed a commit to discord/react-native that referenced this pull request Jan 11, 2024
…info if possible (facebook#42079)

Summary:
This is a continuation of my [last PR](facebook#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: facebook#42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
huntie pushed a commit that referenced this pull request Jan 15, 2024
…info if possible (#42079)

Summary:
This is a continuation of my [last PR](#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: #42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
hurali97 added a commit that referenced this pull request Jan 15, 2024
Apply stack Symbolication patch part 1 & 2 to 0.71-stable (#41377) (#42079) (original #40914)
Flewp pushed a commit to discord/react-native that referenced this pull request Mar 9, 2024
facebook#41377)

Summary:
For a very long time when a promise rejects without an attached catch we get this warning screen without a correct stack trace, only some internal calls to the RN internals.

<img src="https://github.com/facebook/react-native/assets/1634213/75aa7615-ee3e-4229-80d6-1744130de6e5" width="200" />

I created [an issue for discussion](react-native-community/discussions-and-proposals#718) in the react-native-community repo and we figured out it was only a matter of symbolication. While it cannot be done on release without external packages and source maps, at least while developing we can provide a symbolicated stack-trace so developers can better debug the source of rejected promise.

I got the stack trace symbolicated and the correct code frame. I'm missing some help trying to display it in the warning view but at the very least I can now correctly show the line of the error and log the codeframe to the console.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [FIXED] - Show correct stack frame on unhandled promise rejections on development mode.

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: facebook#40914

Test Plan:
I simply created a throwing function on a dummy app, and checked the output of the console and the warning view:

```ts
import React from 'react';
import {SafeAreaView, Text} from 'react-native';

async function throwme() {
  throw new Error('UNHANDLED');
}

function App(): JSX.Element {
  throwme();

  return (
    <SafeAreaView>
      <Text>Throw test</Text>
    </SafeAreaView>
  );
}

export default App;
```

Here is the output

<img src="https://github.com/facebook/react-native/assets/1634213/2c100e4d-618e-4143-8d64-4095e8370f4f" width="200" />

Edit: I got the warning window working properly:

<img src="https://github.com/facebook/react-native/assets/1634213/f02a2568-da3e-4daa-8132-e05cbe591737" width="200" />

Reviewed By: yungsters

Differential Revision: D50324344

Pulled By: javache

fbshipit-source-id: 66850312d444cf1ae5333b493222ae0868d47056

Co-authored-by: Oscar Franco <[email protected]>
Flewp pushed a commit to discord/react-native that referenced this pull request Mar 9, 2024
…object stack info if possible (facebook#42079)

Summary:
This is a continuation of my [last PR](facebook#40914) which improved the symbolication of unhandled promise rejections.

While I was developing another library I noticed I still got an error stack of the log adding and not of the error itself. The library I'm trying to debug does not throw a standard error object but rather a custom one, but it still contains the stack field. By passing this stack field to the logbox call I was able to get a better symbolicated stack trace. The exact line of the failure is not displayed but at least the correct file is.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[GENERAL] [ADDED] - Unhandled promise rejection - attach non-standard Error object stack info if possible

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: facebook#42079

Test Plan:
Test any unhandled promise rejection with a non-standard error (line 23, toString must not return `[object Error]`) and see if the correct (or at least a better) stack trace is shown.

Here is the one I got before and after this change:

<img src="https://github.com/facebook/react-native/assets/1634213/3d07faad-9535-42c9-8032-b4d8fe407e88" width="200" />

<img src="https://github.com/facebook/react-native/assets/1634213/2c39bd82-c7a1-4f58-8ac4-5c479bb96b6e" width="200" />

Reviewed By: huntie

Differential Revision: D52431711

Pulled By: cipolleschi

fbshipit-source-id: be2172d3b1e2fc3f72812faac372c83bc6dface2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants