78

I'm upgrading my Angular2 app from 2.0.0 to 2.3.0, and I'm running into the following error. Any ideas as to why? I saw this other post (Angular 2.0.1 Router EmptyError: no elements in sequence), but after trying this, the issue still remains. What causes this error?

Error: Uncaught (in promise): EmptyError: no elements in sequence
Error: no elements in sequence
    at EmptyError.ZoneAwareError (zone.js:672)
    at new EmptyError (EmptyError.ts:13)
    at FirstSubscriber._complete (first.ts:161)
    at FirstSubscriber.Subscriber.complete (Subscriber.ts:122)
    at Subject._subscribe (Subject.ts:109)
    at Subject.Observable.subscribe (Observable.ts:98)
    at Observable._subscribe (Observable.ts:158)
    at Observable.subscribe (Observable.ts:98)
    at Observable._subscribe (Observable.ts:158)
    at FirstOperator.call (first.ts:82)
    at Observable.subscribe (Observable.ts:96)
    at Object.subscribeToResult (subscribeToResult.ts:32)
    at MergeAllSubscriber._next (mergeAll.ts:82)
    at MergeAllSubscriber.Subscriber.next (Subscriber.ts:95)
    at MapSubscriber._next (map.ts:80)
    at resolvePromise (zone.js:475) [angular]
    at resolvePromise (zone.js:460) [angular]
    at /libs/zone.js/dist/zone.js:509:17 [angular]
    at Object.onInvokeTask (core.umd.min.js:32) [angular]
    at ZoneDelegate.invokeTask (zone.js:261) [angular]
    at Zone.runTask (zone.js:151) [<root> => angular]
    at drainMicroTaskQueue (zone.js:405) [<root>]
    at ZoneTask.invoke (zone.js:336) [<root>]ErrorHandler.handleError @ core.umd.min.js:31next @ core.umd.min.js:32generatorOrNext.object.schedulerFn @ core.umd.min.js:32SafeSubscriber.__tryOrUnsub @ Subscriber.ts:238SafeSubscriber.next @ Subscriber.ts:190Subscriber._next @ Subscriber.ts:135Subscriber.next @ Subscriber.ts:95Subject.next @ Subject.ts:61EventEmitter.emit @ core.umd.min.js:32NgZone.triggerError @ core.umd.min.js:32onHandleError @ core.umd.min.js:32ZoneDelegate.handleError @ zone.js:233Zone.runGuarded @ zone.js:129_loop_1 @ zone.js:416drainMicroTaskQueue @ zone.js:425ZoneTask.invoke @ zone.js:336

I've also noticed that a NavigationError object is thrown when I try to change to a route that uses Guards. But only the error above is what is shown in the console.

NavigationError {id: 2, url: "/home", error: EmptyError}

I'm kind of at a loss and would appreciate any help.

Community
  • 1
  • 1
hartpdx
  • 2,230
  • 2
  • 15
  • 18

14 Answers14

98

In my case I've got those errors when I used first() in an Observable that had a takeUntil() emitted before first().

Example:

let test$ = new ReplaySubject(1);
let testAux$ = new ReplaySubject(1);
let test2$ = test$.asObservable().takeUntil(testAux$.asObservable());
test2$.first().subscribe(() => console.log('first!'));
testAux$.next(null);

It was hard to discover the problem because I returned an observable with takeUntil() in a service that was used by a different class (in a different file) that called first(), and the error was received during page navigation (because takeUntil() received an observable that emitted when the previous page was destroyed), and it appeared as the problem was in the navigation itself.

The weirdest thing is that the error happens when the next() is called, and not in the 2nd argument of subscribe(), causing the navigation process itself to fail. I don't know if this feature was chosen by the rxjs team to behave this way, because it makes much more difficult to isolate the problem, and, the main problem, the producer receives an error because of something done by a consumer, which seems as a bad design to me.

According to this comment:

.first() will emit exactly one item or throw an error[...] If you would like to get at most one item from the observable, use .take(1).

Update (2020-08-31)

It seems that the error can actually be handled by including a 2nd argument to receive errors in the subscription, like in the following case:

const test$ = new ReplaySubject(1);
const testAux$ = new ReplaySubject(1);
const test2$ = test$.asObservable().takeUntil(testAux$.asObservable());
test2$.first().subscribe(() => console.log('first!'), () => console.log('first error'));
testAux$.subscribe(() => console.log('another'), () => console.log('another error'));
console.log('1');
testAux$.next(null);
console.log('2');

That logs:

1
first error
another
2

(in this case you wouldn't see the No elements in sequence error)

But if you want to receive at most 1 item (but it would still be fine to receive none), then take(1) would still be the better approach, and it is what I would recommend by default, instead of first().

Lucas Basquerotto
  • 7,260
  • 2
  • 47
  • 61
  • 13
    Changing a first() to a take(1) as recommended here fixed the similar error message "Error: no elements in sequence" for me. – encrest Mar 30 '18 at 17:19
  • 3
    In my case, a component was calling `.first()` on an empty Observable (the Observable was returned by a mock implementation of a service, so it only failed in unit tests). – mamacdon Oct 04 '18 at 22:50
  • @mamacdon same here. Good catch! – LppEdd Jul 30 '19 at 13:47
  • This thing happens easily when you have something in your `ngAfterViewInit` that does a `takeUntil(this.destroy$), first()`. And in your destroy, you trigger your `destroy$` flag. It would happen if you load a component, but somehow, you delete it quickly from a parent component in a `ngAfterViewInit`. Review all your `takeUntil(this.destroy$)` followed by `first()` and replace it with `take(1)`. – jsgoupil Jun 20 '20 at 20:22
81

This error happens when using RxJS 5.5.3 with angular (version 4/5), so just skip RxJS 5.5.3, and use RxJS 5.5.4 by adding "rxjs": "^5.5.4" to your project package.json.


Before RxJS 5.5.4 came out answer: (old, don't do this)

  • Lock version 5.5.2 RxJS on package.json source
  • add pathMatch: 'full' on empty paths source

It looks like this is an RxJS issue which should be patched pretty soon. source

golfadas
  • 5,351
  • 3
  • 32
  • 39
21

You need change to:

canActivate(route: ActivatedRouteSnapshot): Observable<boolean>{
   return new Observable<boolean>(resolve => {
      resolve.next(true);
      resolve.complete();
  });
}

================

I am unsure why but for me this error was caused by CanActivate router guards using observables. Moving to promises fixed the issue however.

I moved from this:

canActivate(route: ActivatedRouteSnapshot): Observable<boolean>{
     return new Observable<boolean>(resolve => {

          resolve.complete(true);
        //or
          resolve.complete(false);

    });
}

to this:

canActivate(route: ActivatedRouteSnapshot){
     return true;
        //or
          return false;

    });
}
ibuler
  • 13
  • 4
Victor96
  • 9,102
  • 1
  • 13
  • 13
  • I switched my guards over to Promises to try this out. It works! I am at a loss as to why that would matter, but I'm going to roll with this. Thank you Victor96! – hartpdx Dec 15 '16 at 23:20
  • 2
    Change resolve.complete(true); to resolve.next(true); and it should work. As per https://angular.io/docs/ts/latest/guide/router.html#!#guards >Accordingly, a routing guard can return an Observable or a Promise and the router will wait for the observable to resolve to true or false. And complete does not return any element but next does. – skin Dec 16 '16 at 03:17
  • For me, `resolve.next(true); resolve.complete()` did not work, but using a `Promise` did. – Lars Mar 17 '17 at 14:51
  • 1
    observer.complete(value) does nothing... you wanted to do `observer.next(true); observer.complete` or simply `Observable.of(true)`. This answer is unfortunately bad advice. – Ben Lesh Nov 20 '17 at 23:47
10

Had the same issue because I was doing a .first() after a this.http.get(); Http.get returns a cold observable so first was unnecessary because cold observables unsubscribe themselves automatically.

this.http.get().pipe(first()); -> EmptyError: no elements in sequence

The Segfault
  • 939
  • 1
  • 10
  • 23
8

For anyone coming here because of the error message, this was happening to us because we were returning a void Observable. You need to return at lease some value like "true".

return of(true);
carraua
  • 1,398
  • 17
  • 36
7

Add this property to your route.

pathMatch: 'full

for eg.

{ pathMatch: 'full', path: "", component: HomeComponent }

It worked for me.

knigalye
  • 956
  • 1
  • 13
  • 19
  • 1
    There is no need to change app routes in order to fix this (as of today). A new version of RxJS came out with a fix for this error. Upgrade RxJS to version 5.5.4. – golfadas Dec 06 '17 at 09:44
6

I will also share my experience with this error in RxJS 6.5.3 when using the last() operator. The problem could be solved by applying a default value like:

.pipe(
  last(null, []) // predicate = null, defaultValue = []
)
scipper
  • 2,944
  • 3
  • 22
  • 45
2

I had this issue in Angular 6 / RxJs 6 and found it was due to my guard having a redirect and complete right after it.

Before:

if (!res) {
  this.router.navigate(['/']);
  observer.complete(); // <-- This was the offender
}

After:

if (!res) {
  this.router.navigate(['/']);
}
Michael
  • 316
  • 3
  • 18
2

In my case, using version 5.5.12 of rxjs, I had trouble with this in a CanActivate scenario. But the solution for me was very easy in the end. I changed my code from

const guard: Subject<boolean> = new Subject<boolean>();

to

const guard: ReplaySubject<boolean> = new ReplaySubject<boolean>(1);

Calling

guard.next(...);
guard.complete();

later. That did the trick. So no .first nor promises needed in my scenario.

timtos
  • 2,225
  • 2
  • 27
  • 39
0

You have to return the whole response instead of response.body

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/30553561) – Jacob Nelson Dec 14 '21 at 16:27
0

As this page is the first Google result for this error, below is another pattern that demonstrates the underlying issue more generally.

Having gotten very comfortable with observables and rxjs operators, going back to a promise-based approach had me scratching my head:

await lastValueFrom(myConfirmation().pipe(
  filter(confirmation => !!confirmation),
  switchMap(() => myOperation())))
  .then(response => console.log(response))
  .catch((error) => console.log(error));

If confirmation evaluates to false, the then block is never reached, the promise is not able to complete and an EmptyError: no elements in sequence error is thrown.

The solution in this case is to remove the filter, so that the then block is still reached, even if confirmation evaluates to false.

await lastValueFrom(myConfirmation().pipe(
  switchMap((confirmation) => {
    if (confirmation) {
      return myOperation();
    } else {
      return of(false)
    }
  })))
  .then(response => {
    if (response) {
      console.log(response);
    } else {
      console.log('Operation was not confirmed');
    }
  })
  .catch((error) => console.log(error))
Matt Saunders
  • 3,538
  • 2
  • 22
  • 30
0

I got this error because of Angular's router expandSegment function which uses a RXJS first operator.

It was occurring because a canDeactivate handler for the component was miss handled and was returning an already complete observable (for a dialog), causing the router to complete without having anything emitted, thus resulting in the error from first.

The fix for me was to move a line of code that cleared the old dialog observable from a variable, from a tap (which didn't run if an error happened or it completes too quickly) to a finalize operator, which would always run.

My particular situation was I have a can deactivate that checks if user has unsaved changes on the page and asks whether to discard or stay on the page. If the app was in the middle of saving those changes and they pick discarded, then something weird happens and the bit of code that clears the dialog observable (it records it so can pass to subsequent canDeactivates, so don't get multiple dialogs popups) never runs and each subsequent call to the canDeactivate gets immediately completed and causes the error.

0

I got this error when toPromise() was deprecated and I started using lastValueFrom() instead.

In my case, I was cancelling API calls if one was already under way:

  private async loadBoard() {
    this.cancelBoardFetch.next();
    const board = await lastValueFrom(this.service.getBoard().pipe(
      takeUntil(this.cancelBoardFetch)
    ));

    // This errors if cancelBoardFetch is called during an existing request.
  }

I changed it to use startWith() to ensure something was always returned:

  private async loadBoard() {
    this.cancelBoardFetch.next();
    const board = await lastValueFrom(this.service.getBoard().pipe(
      startWith(undefined),
      takeUntil(this.cancelBoardFetch)
    ));

    // Add a check to see if we cancelled, if so do nothing.
    if (!board) {
      return;
    }

    // Do something with the result
    ...
  }
adam0101
  • 29,096
  • 21
  • 96
  • 174
-1

None of the above worked for it. This is what worked for me. In the route definition:

{ path: '', component: WelcomeComponent, pathMatch: 'full' },
cfnerd
  • 3,658
  • 12
  • 32
  • 44