2

After searching on SO, I could not find an answer to this problem. All of the questions I saw, the error was still being caught. I'm having the inverse problem. No matter how I refactor the following code, I am STILL not able to catch the error.

I hope I'm just overlooking something or doing something wrong.

process.on("uncaughtException", uncaughtExceptionListener)

start().catch(console.error)

async function start() {
  try {
    await queue.connect()
  } catch (err) {
    return console.error('Some Error:', err.message)
  }
}

// queue.connect in class
async function connect() {
  try {
    // The next line is where the error originates
    await client.connect()
    return console.log('Never makes it here')
  } catch (err) {
    // Never makes it here either
    return console.error('[client] Connection failed!')
  }
}

Output:

node:internal/process/promises:391
    triggerUncaughtException(err, true /* fromPromise */);
    ^

Error: connect ECONNREFUSED 127.0.0.1:6090

I've rewritten connect() as simple as possible in multiple different ways:

function connect() {
  const p2 = new Promise((resolve, reject) => {
    client.connect().then(resolve).catch(reject)
  })

  p2.then(n => {
    // ...
  }).catch(e => {
    console.log('Never makes it here')
  })

  return p2
}

And even more simply:

async connect() {
  try {
    // The next line is where the error originates
    await client.connect()
  } catch (err) {
    return console.log('Never makes it here')
  }
}

Replacing await client.connect() with throw new Error('client.connect() simulation') Everything works as it should. What is client.connect() doing, where the exception can not be caught?

Going as far as encapsulating the actual .connect method, the exception still happens and node crashes. Example:

client.connect().then(() => {}).catch(err => console.error('We never make it here'))

Version: (Although still happens on latest node version)

$ node --version
v20.18.0

Client in question: bee-queue
bee-queue.connect function

3
  • 1
    Your edit says « Replacing client.connect() with throw new Error('client.connect() simulation') », but I surmise that you replaced the whole await client.connect by your throw (anyway await throw... is not valid JS). So, yes, your throw is synchronous. It does happen during the execution of connect. But client.connect is not. The throw that occurs during its operation does not occur during the execution of client.connect() (which is just a function that returns a promise. So unless the fabrication of that promise fails...). Commented Nov 18, 2024 at 16:11
  • Yes, it's an edit typo/error in my summary. I'm not await'ing the throw here. The point still stands. Yes I'm using proper coding techniques and methods (not a complete novice here lol). If you look at my other examples, no matter how I handle the client.connect I get an exception, and catch no matter the context never catches the exception. Commented Nov 18, 2024 at 16:41
  • Yes, sure. Again, client.connect is asynchronous. That means that all it does, is creating a promise. You would catch the exception if the creation of the promise were to fail (but of course, it can't). But not if, later, well after client.connect has returned, the fullfilling of the promise fails. I know that with async/await this is hidden, and code appears to be the one of connect, not the one executed when the promise if fullfilled (or, worse, of a continuation of a continuation of a ..., of that promise, since each await you have is that). Yet, it is just a promise. Commented Nov 18, 2024 at 21:20

3 Answers 3

1

The thing is, async/await offers a way to write a chain of promises/then (of even just callbacks) with a simple writing, as if all asynchronous operations were synchronous. But, still, under the hood, they are still asynchronous.

Just an over simplification of your example

async function connect(){
    throw "failed connect";
}

async function start(){
    await connect();
    dosomethingelse();
}

start();

It is important to understand, that this is the equivalent of

function start(){
     connect().then(function(){
         dosomethingelse();
     })
}

It is a wonderful improvement to JS, since sometimes you may have a sequence of 10 asynchronous operations to perform. So it is convenient to write them in sequence with just a plain ; separator, as if it was a simple connect(); dosomething();, when in reality is connect(continuation); end this function, continuation being that do something. But, still, it is a bunch of promises and then

So, if you add a try/catch block now

async function connect(){
    throw "failed connect";
}

async function start(){
    try{
        await connect();
    }catch(e){
        console.log("catch", e);
    }
}

Well, it is still in reality

    try{
        connect().then(continuation)
    }catch(e){
        ...
    }

the try englobes only the calls to asynchronous connect

So a little bit like

try{
    setTimeout(trytoconnect, 1000);
}catch(e){
}

wouldn't catch anything happening in trytoconnect, your try/catch has no reason to catch what happens in your connect (unless the error happens in the creation of the promise itself, not in its execution)

As for a solution, you can find one here

function okOrError(promise){
    return promise.then((arg) => {return {ok: arg}}).catch((arg) => {return {error: arg}});
}

async function connect(){
    if(Math.random()<0.5) return "success";
    throw "failed connect";
}

async function start(){
    let x=await okOrError(connect())
    if(x.error) throw x.error;
    console.log(x.ok);
}


start();
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your answer. I'm not understanding what you're pointing out here. If you're saying my issue is syntax - how is it the last example with a try/catch directly around await client.connect() not working? I tried your example and the exception is still thrown.
Clarification: I tried your example with client.connect and the exception is still thrown.
Also, to be clear - the issue you linked to suggests I have a try/catch around synchronous method, and the error is thrown async. This is not the case in my example because I AM using async/await - which is what the solution is to the issue you mentioned.
Because it is a await. await this(); somestuffafter is a short cut for this().then(function(){somestuffafter}. Which is very convenient. Because await a(); await b(); await c(); d(); is easier to write than a().then(ffunction(){ b().then(function(){ c().then(function(){ d(); }) }) }); Still, your catch would call only what happens during a(). And nothing happens during a() (but the fabrication of a promise by the interpreter, following async/await rules.
So again. client.connect() does nothing (but creating a promise to do something). And, importantly for your question, doesn't raise any exception. It is the fullfilling of its promise (later, in another execution context) that will do things, and sometimes raise an exception.
0

Solved. The issue was not in my code. The issue was upstream in the bee-queue library. The promise was not being caught and rejected to the downstream promise chain.

See this PR for more info: https://github.com/bee-queue/bee-queue/pull/878

Comments

0

Let me say this... You're not doing it right...

async functions always will return a promise. Then you must use .then() and/or .catch() on the async function call, even if you await for the fulfillment. try/catch will never get the error because they run outside of this block context.

Here's a working example:

// Let's say you have an async function...
async function doSomething ()
{
  // And it do something that causes an error.
  window.inexistentMethod();
}

// If you want to catch the error...
async function doTest()
{
  const value = await doSomething()
    .then
    (
      result =>
      {
        console.log('Result:', result)
        
        // What will go into `value`.
        return result ?? true;
      }
    )
    .catch
    (
      error =>
      {
        console.warn('Error:', error.message);
        
        // What will go into `value`.
        return false;
      }
    );
  
  console.log('Final value:', value);
}

// TEST THE ERROR:
doTest();

NOTE that .then() and .catch() both return a different promise than the one they're called upon. So, in the above example, first is parsed doSomething() that returns a promise, then .then() that returns another promise that fulfills if there's no error in doSomething() and .then(), then .catch() that returns another promise that fulfills if doSomething() and .then() produces no error or call the callback on .catch if there's any error and then returns what the callback returned. Only then, the await will retrieve the promise return value.

There are those two other answers I gave to other questions that shows how to handle unhandled errors that may come from setTimeout, events and other async sources:

https://stackoverflow.com/a/79189671/2804429

https://stackoverflow.com/a/79184566/2804429

5 Comments

Please use my code examples to clearly point out WHERE I have gone wrong. That said, the issue wasn't in my code. See my posted answer. The issue was with the library being used not catching the promise chain.
@LeviRoberts I'm sorry, what I meant to say you did wrong is how you're trying to catch the error with try/catch blocks... In this specific case, you didn't even mention this third party library. My answer is still valid on how to catch an error from an async function call though... Your problem is that that async function call is making another async call and is not handling the error. The two links at the end of my answer can help you catch those unhandled errors...
I'm sorry, you are mistaken. I did mention the library. Client in question: bee-queue bee-queue.connect function. I added that as an edit 2 hours ago. Your answer is from 42 minutes ago.
@LeviRoberts Oh, that's because I didn't see the edit. I took more than 1hr to finish my answer as I paused in the middle to do something else... But nevertheless... I just hope my answer can help you, even if it's just a little.
My apologies then. For what it's worth, I most definitely AM wrapping the await in a try/catch block. That is all that's required to catch a promise, which is what async is - syntactic sugar. What was happening is that the client library was making a separate async call and not catching any errors from that. My PR fixes that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.