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

Exception not thrown when maximum call stack is exceeded #5370

Closed
reline opened this issue Apr 1, 2020 · 5 comments · Fixed by #6524
Closed

Exception not thrown when maximum call stack is exceeded #5370

reline opened this issue Apr 1, 2020 · 5 comments · Fixed by #6524
Assignees

Comments

@reline
Copy link

reline commented Apr 1, 2020

Bug Report

Current Behavior
When subscribing to a stream that emits errors indefinitely and returning the original source when catching said error, eventually the maximum call stack is exceeded. I expect an exception to be thrown in this case, but it is currently swallowed and printed to the console.

Reproduction

import { of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';

const observable = of(4).pipe(
  map(n => {
      if (n === 4) {
        throw 'four!';
    }
    return n;
  }),
  catchError((err, source) => source),
);
 
const testScheduler = new TestScheduler((actual, expected) => {
  // using jest
  expect(actual).toMatchObject(expected);
});
 
// This test should fail due to Maximum call stack size exceeded
it('should fail', () => {
  testScheduler.run(helpers => {
    const { expectObservable } = helpers;
    expectObservable(observable).toBe('-');
  });
});
 PASS  __tests__/callstack-test.ts
  ✓ should fail (14ms)

  console.warn node_modules/rxjs/internal/Observable.js:55
    RangeError: Maximum call stack size exceeded
        at new SourceMapConsumer (/dev/node_modules/source-map-support/node_modules/source-map/lib/source-map-consumer.js:17:22)
        at mapSourcePosition (/dev/node_modules/source-map-support/source-map-support.js:190:14)
        at wrapCallSite (/dev/node_modules/source-map-support/source-map-support.js:367:20)
        at Function.prepareStackTrace (/dev/node_modules/source-map-support/source-map-support.js:416:39)
        at prepareStackTrace (internal/errors.js:37:29)
        at formatError (internal/util/inspect.js:907:19)
        at formatRaw (internal/util/inspect.js:703:14)
        at formatValue (internal/util/inspect.js:591:10)
        at inspect (internal/util/inspect.js:221:10)
        at formatWithOptions (internal/util/inspect.js:1693:40)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.302s, estimated 2s

Expected behavior
I would like the exception to be thrown and for this test to fail.

Environment

  • Runtime: Node v12.16.1
  • RxJS version: 6.5.4

Additional context/Screenshots
I see in #3803 that the warn was added instead because throwing new errors would have been a breaking change. Could this be considered for 7.x?

@cartant
Copy link
Collaborator

cartant commented Apr 1, 2020

This is by design - to avoid a breaking change. See #4089

@benlesh in which major version will it be okay to change this to throw using hostReportError?

Also, even when hostReportError is used, your test will probably not behave as you expect because the error will be thrown asynchronously.

@benlesh
Copy link
Member

benlesh commented May 14, 2020

Observables should always be assumed asynchronous in tests, because we have to handle our errors as such. Anytime someone does a try { observable.subscribe(); } catch (err) {} they're relying on luck, as the observable should be treated as a black box.

The ONLY source of truth for observable errors should be the error handler in subscriptions.

The fix for the max call stack error here might be to catch at the subscription point. These sorts of things are weird to defend against.

Also of note: infinitely retrying or repeating a synchronous observable is probably a bad idea.

@benlesh
Copy link
Member

benlesh commented May 14, 2020

Yeah... @cartant, it looks like we're not catching errors that happen during subscription when subscribing to observables that were created via lift. Other observables are using _trySubscribe, which will catch errors and send them down the proper path.

@benlesh benlesh self-assigned this Jun 9, 2020
@benlesh
Copy link
Member

benlesh commented Jun 9, 2020

Looking into this, I think this is going to add a bit of weight to an already bloated hot-path.

As it stands, we have a lot of logic around error handling during subscription that is just there for legacy reasons to keep us from breaking our partners at Google. (see _enable_super_gross_mode_that_will_cause_bad_things)... That truly MUST go by v8, and I think that will be the appropriate time to fix this, since it's truly an edge case, IMO.

@benlesh benlesh added the AGENDA ITEM Flagged for discussion at core team meetings label Jun 9, 2020
@benlesh
Copy link
Member

benlesh commented Jul 2, 2020

I think with this one, what we want to do is probably add some tests that we skip for now, but have resolved by version 8, when we do a more comprehensive refactor of the library.

benlesh added a commit to benlesh/rxjs that referenced this issue Jul 2, 2020
benlesh added a commit that referenced this issue Jul 3, 2020
* test(Observable): add skipped test for edge case

related #5370

* chore: fix lint
@benlesh benlesh removed the AGENDA ITEM Flagged for discussion at core team meetings label Jul 29, 2020
benlesh added a commit to benlesh/rxjs that referenced this issue Jul 14, 2021
- Uncomments a skipped test that was fixed with the v7 refactor (apparently just via better architecture)
- Adds an additional test to simplify the same assertion
- Adds a comment above a skipped section of tests explaining that we can probably remove them in v8 after we've moved completely to newer multicasting paradigms and removed deprecated operators.

Resolves ReactiveX#5370
benlesh added a commit that referenced this issue Jul 21, 2021
* chore: Stop skipping valid test

Resolves #5105

* chore: Add comment about why this test is skipped.

* chore: Correct skipped tests

- Some of the skipped tests were clearly copied from retryWhen but never updated appropriately.
- One of the skipped tests showed a buggy behavior that is now reported in an issue and a comment with a link to that issue is now above it.

* chore: Update skipped tests

- Uncomments a skipped test that was fixed with the v7 refactor (apparently just via better architecture)
- Adds an additional test to simplify the same assertion
- Adds a comment above a skipped section of tests explaining that we can probably remove them in v8 after we've moved completely to newer multicasting paradigms and removed deprecated operators.

Resolves #5370

* chore: Stops skipping a fixed test.

* chore: Add commented out bit to help find skipped tests

Figuring out what tests are skipped out of 3,000+ tests is pretty annoying. I want to leave this here as a sign post to help.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants