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

MergeMap throws uncatchable errors #5344

Closed
kondi opened this issue Mar 5, 2020 · 7 comments
Closed

MergeMap throws uncatchable errors #5344

kondi opened this issue Mar 5, 2020 · 7 comments
Labels
bug Confirmed bug

Comments

@kondi
Copy link

kondi commented Mar 5, 2020

Bug Report

Current Behavior
In the sample code below, the TypeError is not caught in the catchError callback, but thrown globally. We can not detect the error in the observable stream and in NodeJS environment this error can cause the whole application to shut down.

(If the provided mergeMap callback throws an Error instead of returning null, the error is handled correctly.)

Reproduction

of(1).pipe(
  // Without the next line, the error is caught correctly
  mergeMap(async () => 2),
  // This usage should produce a runtime TypeError: You provided 'null' where a stream was expected.
  mergeMap(() => null),
  catchError(error => {
    console.error('Error in the stream caught:', error.message);
    return [];
  })
).subscribe();

Expected behavior
The TypeError should not leave the observable stream, should be catchable by catchError the same way like when the mergeMap(async () => 2) line does not present.

Environment

  • Runtime: Node v12.10.0, Chrome v80.0.3987.122
  • RxJS version: 6.5.4

Possible Solution
Not a solution, just a very hacky workaround for somebody who is affected by this bug: https://stackblitz.com/edit/rxjs-uncaught?file=patch.ts

@cartant cartant added the bug Confirmed bug label Mar 5, 2020
@Andrei0872
Copy link

Andrei0872 commented Mar 8, 2020

I think I found where the problem comes from:

subscribeToPromise:

export const subscribeToPromise = <T>(promise: PromiseLike<T>) => (subscriber: Subscriber<T>) => {
  promise.then(
    (value) => {
      if (!subscriber.closed) {
        subscriber.next(value); // Here's the problem! 
        subscriber.complete();
      }
    },
    (err: any) => subscriber.error(err)
  )
  .then(null, hostReportError);
  return subscriber;
};

The problem is subscriber.next(value);, where subscriber is an InnerSubscriber, which will notify the outer subscriber(mergeMap in this case), which will in turn push the value further, meaning it will reach the second mergeMap.

Because subscriber.next(value) will cause an error, it will be intercepted by hostReportError, from the second then, which will throw the error like this: setTimeout(() => { throw err; }, 0);.

Here's a simplified example:

Promise.resolve(2)
    .then(
        v => {
            produceErr();
        },
        err => console.log('ERR') // Never reached
    )
    .then(null, hostReportError);

function produceErr () {
    throw new Error('err!');
}

function hostReportError () { console.log('caught!') };

I think one solution would be to surround that logic with a try/catch

try {
 subscriber.next(value);
  subscriber.complete();
} catch (err) {
  subscriber.error(err);
}

@tonivj5
Copy link
Contributor

tonivj5 commented Jul 22, 2020

Is there a workaround? I'm suffering from this and I'm losing any stacktrace from the original error

@tonivj5
Copy link
Contributor

tonivj5 commented Jul 22, 2020

I thought my problem was other, related, but different of this issue...

Here is the reproduction of the problem I had: https://stackblitz.com/edit/rxjs-uncaught-jirbiw?file=index.ts

I forget to return an observable on the else branch into catchError operator and Typescript didn't warn me, and runtime error didn't help too much 😕 I thought error comes from mergeMap (because I'm using it with promises), but it was an unfortunate coincidence 😆

Should I open other issue?

@jakovljevic-mladen
Copy link
Member

It seems that this problem is not reproducible as of v7 (7.0.0-beta.13). No matter if mergeMap(async () => 2) call is included or not, a callback passed to the catchError is always called (if mergeMap(() => null) is included). Also, mergeMap(() => null) won't work (it produces a TS error), so an explicit cast has to be performed: mergeMap(() => <any>null)

@tonivj5 In your case from stackblitz, not having the return statement produces runtime error in v7.

@cartant could you verify and possibly close this issue?

@cartant
Copy link
Collaborator

cartant commented Mar 28, 2021

@cartant could you ... possibly close this issue?

Not really. The problem is reproducible in 6.6.6, so it exists in the current non-beta release and is a bug. It should be fixed, but it's not at the top of my prioritised list of things that need to be done. However, I suspect that my curiosity will get the better of me and that I'll end up fixing it soonish.

cartant added a commit to cartant/rxjs that referenced this issue Mar 28, 2021
@jakovljevic-mladen
Copy link
Member

Thanks @cartant for the confirmation and for the fix.

benlesh pushed a commit that referenced this issue Mar 28, 2021
* test: add failing invalid ObservableInput tests

* fix: catch within innerSubscribe

Closes #5344
@cartant
Copy link
Collaborator

cartant commented Mar 28, 2021

Closed by #6186

@cartant cartant closed this as completed Mar 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Confirmed bug
Projects
None yet
Development

No branches or pull requests

5 participants