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

onunhandledrejection for globalThis? #7013

Closed
nzakas opened this issue Aug 11, 2020 · 23 comments · Fixed by #12994
Closed

onunhandledrejection for globalThis? #7013

nzakas opened this issue Aug 11, 2020 · 23 comments · Fixed by #12994
Assignees
Labels
cli related to cli/ dir feat new feature (which has been agreed to/accepted) web related to Web APIs

Comments

@nzakas
Copy link

nzakas commented Aug 11, 2020

I was looking for an equivalent to the Node.js "unhandledrejection" event in Deno, but it appears there isn't one on globalThis.

There does appear to be an onunhandledrejection event handler defined on workers, though.

Is there an equivalent event outside of workers, and if not, could there be? 😄

@kitsonk
Copy link
Contributor

kitsonk commented Aug 11, 2020

We should have it, it is part of the HTML Living Standard: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onunhandledrejection

@nzakas
Copy link
Author

nzakas commented Aug 11, 2020

Maybe I'm testing for it wrong? Here's what I tried:

globalThis.onunhandledrejection = function() {
    console.error("Unhandled rejection");
};

new Promise(() => {
    throw new Error("Error");
});

@lucacasonato
Copy link
Member

I think @kitsonk means we should add it, not we should already have it :-)

@nzakas
Copy link
Author

nzakas commented Aug 11, 2020

@lucacasonato oh gotcha 😆

@lucacasonato lucacasonato added cli related to cli/ dir suggestion suggestions for new features (yet to be agreed) web related to Web APIs labels Aug 17, 2020
@andreespirela
Copy link

@lucacasonato Wouldn't this mean that if added, we will now have the option to stop Deno from dying in uncaught exceptions?

@lucacasonato
Copy link
Member

Uncaught exceptions are not the same as uncaught rejections. This is for when you don't define a .catch on a promise.

@andreespirela
Copy link

Uncaught exceptions are not the same as uncaught rejections. This is for when you don't define a .catch on a promise.

Got it, didn't notice it was "rejection", Thanks

@nayeemrmn
Copy link
Collaborator

Uncaught exceptions are not the same as uncaught rejections. This is for when you don't define a .catch on a promise.

@lucacasonato I think uncaught rejections absolutely fall under "uncaught errors".

Wouldn't this mean that if added, we will now have the option to stop Deno from dying in uncaught exceptions?

Let's say we tried to mirror the browser behaviour by not exiting on a rejection if and only if there is an uncaughtrejection handler which calls rejectionEvent.preventDefault(). Does that count as "catching" the error in a way? If so, we're not breaking our rule.

Nevertheless, I'm against this being usable to prevent exits on rejections. I think we are still breaking our rule at the language level and this is a foot-gun. Note that browsers don't kill the tab on error either way, they only use this to decide whether or not to output the error to the console. So it's not so simple to draw an analogy.

@Skillz4Killz
Copy link

Is there any update/eta on this? This is an absolutely crucial feature to have to prevent crashes.

@lucacasonato
Copy link
Member

lucacasonato commented Nov 15, 2020

@Skillz4Killz This will not be implemented to prevent crashes. If implemented it will only be able to be used to perform synchronous cleanup of allocated resources (e.g. file descriptors, handles, etc). It will not be usable to prevent shutdown on an unhandled rejection. Instead you should catch promise rejections correctly. Deno will always die on unhandled exceptions / rejections.

@Skillz4Killz
Copy link

Skillz4Killz commented Dec 3, 2020

Deno will always die on unhandled exceptions / rejections.

I'd like to request the ability to change this for uncaught promises.

I know a lot of people just say hey if this happens it is your fault. Technically your right. But this is reality. And people make mistakes. Production crashing because of a mistake is a serious issue. Especially when it is so easily preventable. But the problem really gets amplified when even if you are perfect, and your code catches everything and everyone, you can still crash if one of your deps forgets. So you can not just rely on yourself being perfect but the entirety of the world needing to be perfect. This in my opinion makes Deno very hard to want to use in production. Processes crashing are not an acceptable solution imo.

Let's take a look at a simple example. Please bare with me here.

image

In this example, what happens is the process crashes. There is no way for me to catch this error. It's literally impossible. Since c is not returning a promise i can not catch it or try/catch + await it either. Because the developer of that third party dep forgot to catch something, the process crashed. Applying this to a real project, this could be god knows how many deps deep. So the argument that you have to check every line of your deps code isn't really possible realistically. Nor is having to check every single commit of every single dependecy. This just adds on to more reason of why Deno is impossible to use in production.

If this was nested some 10-15 levels deep, we would never be able to debug this and even attempt a fix.

The other day, this actually caused my own project to fail.

image

When I saw this, I was confused out of my mind. What on earth is core.js? I don't have any file called core.js. I don't know of any dep that has this and it sounds like it could literally come from any dep or even Deno itself or even V8. I just don't know. Luckily, my project has very little deps so I went through debugging spendings hours to figure this out. Just to find out there was a bug in listenDatagram(which isn't an issue, it's still unstable). The issue is that this behavior can cause a process in production to crash unknowingly. It becomes a nightmare to debug and solve.

This is all to say that Deno in production is really hard to vouch for. Realistically Deno is only usable in production if you do not use any deps for your projects.


Now for another point of view. Deno is realllllllly hard to use for beginners. A lot of users wanting to use my library are first time developers. This means they are just getting into their very first coding project EVER! I have made it as easy as possible for users like this to be able to get started. They can use the strictest TS settings without even needing to know a thing about typing or casting or anything. However, this 1 issue of them forgetting .catch is a serious issue.

Time and time again I have users reporting their processes crashing because they simply forgot to catch something. Not have a training wheel so to speak makes this really difficult. A simple handler to prevent crashes could be soooo useful because I could have this internally built for them so it alerts them of where they are forgetting to catch so that they can fix their code and prevent their processes from crashing.

@davidpanic
Copy link

I came across this issue because I had a similar cryptic error. Here are my two cents on this.

I myself forgot to await a call to Deno.Conn.write() and then called Deno.Conn.close() which of course crashed the process.

this was my error:

error: Uncaught (in promise) BadResource: Bad resource ID  
    at unwrapResponse (deno:runtime/js/10_dispatch_minimal.js:59:13)
    at sendAsync (deno:runtime/js/10_dispatch_minimal.js:98:12)
    at async write (deno:runtime/js/12_io.js:117:20)

How the hell am I supposed to debug that... I'm honestly surprised I was able to find the source. If that was in a library, quoting @Skillz4Killz

There is no way for me to catch this error. It's literally impossible.

This is an oversight that we as developers can't even deal with! MADNESS!

When I saw this, I was confused out of my mind. What on earth is core.js? I don't have any file called core.js.

I completely agree with @Skillz4Killz here, the error output is not helpful at all with debugging. Just a better stack trace would be enough to help developers debug their code.

The linter didn't tell me that I had an async call without a try {} catch {} or a .catch() at all. It just treats it like it's not an issue when in fact it's almost the worst mistake one can make. Even running deno lint on the file just ignores any unawaited calls.

Deno is impossible to use in production.

Yup. And if it keeps going like this it's only going to get worse. Everybody makes mistakes. At least make them possible to debug.

@canonic-epicure
Copy link

My use case is a testing tool, which is supposed to detect and report any, even unexpected exceptions, raised by the test code. This is not possible if the process synchronously dies on any exception.

@kitsonk kitsonk added feat new feature (which has been agreed to/accepted) and removed suggestion suggestions for new features (yet to be agreed) labels Apr 20, 2021
@johnspurlock
Copy link
Contributor

My use case is a tool that serves as a container for running multiple 3rd-party web handler scripts. I need to be able to keep the server running (and capture and report the error to the user myself) if one of the scripts forgets to catch one of their promises deep in their handler.

Panicking on every unhandled rejection is certainly a reasonable default, but should be configurable either when starting the deno process, or at runtime (e.g. via onunhandledrejection) - to be able to support the use cases that require it.

@chovyprognos
Copy link

I'm just trying to figure out how to restart my deno app when it does crash. Currently I don't see anyway of doing that short of adding node module pm2 (yuck!)

@GCSBOSS
Copy link

GCSBOSS commented Nov 19, 2021

I'm just trying to figure out how to restart my deno app when it does crash. Currently I don't see anyway of doing that short of adding node module pm2 (yuck!)

I believe processes, in general, should not be responsible for restarting themselves. That's the responsibility of the underlying platform the processes are running at (see container orchestration or process managers).

Similarly, in response to other fellows, process crashing in production when there are unhandled exceptions is the safest bet. Since the process should be automatically restarted by its manager and restarting should be the best way to ensure the process is properly initialized again.

@GCSBOSS
Copy link

GCSBOSS commented Nov 19, 2021

My use case is a tool that serves as a container for running multiple 3rd-party web handler scripts. I need to be able to keep the server running (and capture and report the error to the user myself) if one of the scripts forgets to catch one of their promises deep in their handler.

Panicking on every unhandled rejection is certainly a reasonable default, but should be configurable either when starting the deno process, or at runtime (e.g. via onunhandledrejection) - to be able to support the use cases that require it.

Doing something on unhandled rejection/error is the valid use case this issue seems to be about.
Preventing from crashing breaks the wheel.

I don't know the specifics of your project, but at first glance, I'd suggest running those scripts as separate processes. So when they crash your "container" stays alive and deals with the other processes bug as you please.

@bartlomieju bartlomieju self-assigned this Nov 19, 2021
@bartlomieju
Copy link
Member

I will implement this API as it's required for Node compatiblity mode.

bnoordhuis added a commit to bnoordhuis/deno that referenced this issue Nov 27, 2021
Provide a programmatic means of intercepting rejected promises without a
.catch() handler. Needed for Node compat mode.

Also do a first pass at uncaughtException support because they're
closely intertwined in Node. It's like that Frank Sinatra song:
you can't have one without the other.

Stepping stone for denoland#7013.
bnoordhuis added a commit to bnoordhuis/deno that referenced this issue Nov 27, 2021
Provide a programmatic means of intercepting rejected promises without a
.catch() handler. Needed for Node compat mode.

Also do a first pass at uncaughtException support because they're
closely intertwined in Node. It's like that Frank Sinatra song:
you can't have one without the other.

Stepping stone for denoland#7013.
bnoordhuis added a commit that referenced this issue Nov 27, 2021
Provide a programmatic means of intercepting rejected promises without a
.catch() handler. Needed for Node compat mode.

Also do a first pass at uncaughtException support because they're
closely intertwined in Node. It's like that Frank Sinatra song:
you can't have one without the other.

Stepping stone for #7013.
@bartlomieju
Copy link
Member

bartlomieju commented Nov 30, 2021

Note to self:

  • Need to add PromiseRejectionEvent class and its typings
  • Need to setup Deno.core.setPromiseRejectCallback in 99_main.js after runtime initialization that will dispatch PromiseRejectionEvent and check if event was halted (ie. preventDefault() was called on the event)
  • Add defineEventHandler(window, "unhandledrejection"); in 99_main.js
  • If event is not prevented runtime should error out and terminate execution (like it does now).
  • Verify how it will play with deno test subcommand
  • Update MDN compat table

@bartlomieju
Copy link
Member

Landed in #12994, it will be released in v1.24 on July, 20th.

@timfish
Copy link

timfish commented Jul 29, 2022

Did this get reverted and not make it into the release?

@bartlomieju
Copy link
Member

It was released in v1.24 https://deno.com/blog/v1.24#unhandledrejection-event

@timfish
Copy link

timfish commented Jul 29, 2022

Thanks! I couldn't find the relevant commit after the revert but there it is in the release notes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cli related to cli/ dir feat new feature (which has been agreed to/accepted) web related to Web APIs
Projects
None yet
Development

Successfully merging a pull request may close this issue.