ng-recaptcha icon indicating copy to clipboard operation
ng-recaptcha copied to clipboard

ng-recaptcha tag <recaptcha> causes error “zone.js: Unhandled Promise rejection” when route changes

Open kovaljames opened this issue 7 years ago • 47 comments

[Maintainer update 2022-09-26]

Please see this comment.

Summary

I'm submitting a:

  • [x] bug report

Description

I'm using "ng-recaptcha" on Angular component. Im using inside a form like this:

<form class="form-contact"
        [formGroup]="contactForm"
        #formDirective="ngForm" 
        (ngSubmit)="sendMessage(contactForm, formDirective)">
  <re-captcha class="recaptcha"
              formControlName="captcha"
              siteKey="mysitekey">
  </re-captcha>
</form>

Everything seems to be working fine. However, when route changes (ex. user goes to another page) and the AboutComponent is rendered again, an error pops up (multiple times): Unhandled Promise rejection: timeout ; Zone: ; Task: Promise.then ; Value: timeout undefined. I'm 99% sure it's caused by tag because error doesn't show up after removing the tag. Is there a way to render a captcha without errors when route changes (and captcha reloads after im back to component)? Obs: You must wait a few seconds after navigating to another route and back to captcha, then Promise timeout expires and error shows up inside console (google/firefox)

error

https://stackblitz.com/edit/angular-en3spw

Lib versions:

  • ng-recaptcha: 4.0.0-beta.1
  • Angular: 6.1.4
  • Typescript (tsc --version): 2.9.2

kovaljames avatar Aug 30 '18 19:08 kovaljames

It's not necessary to reload a component to get this error - destroying a component containing recaptcha leading to this case PS. This problem is also present in version 3.0.5 of ng-recaptcha

Mikailus avatar Aug 31 '18 12:08 Mikailus

Does anyone know of a work around with this?

ErraticFox avatar Sep 08 '18 21:09 ErraticFox

I had the same error message, but in my case captcha doesn't load anymore ...

I have 2 forms with captcha, first time the captcha render properly, but when i navigate between them captcha doesn't load anymore.

Only workaround i've found : on form component destroy, remove the captcha element from DOM

  @ViewChild('captchaRef')
  reCaptcha: RecaptchaComponent;

...

  ngOnDestroy() {
    const captchaElem = this.reCaptcha['elementRef'].nativeElement;
    captchaElem.parentElement.removeChild(captchaElem);
  }

Now captcha always load properly, but error message continue to show up...

BlueNc avatar Sep 17 '18 05:09 BlueNc

I have the same issue, when the error shows up it breaks some other TS I have running on the next page.

EDIT: I have a guess as to what this is coming from. Seems like the issue is a call to a specific Google URL: https://www.google.com/recaptcha/api2/anchor?[more URL parameters]. Upon navigating away, the library apparently tries to reach this URL, and it just stays at "(Pending)" in dev tools.

BrentACole avatar Sep 28 '18 16:09 BrentACole

Hey folks! Thx for letting me know about this, and I’m sorry that getting back to you took so long.

At this point I think I know where the issue is, ~and I plan to release a fix for it in the next couple of days in the 4.x.x branch.~ and I've reached out to recaptcha support team, see my comment below

DethAriel avatar Sep 30 '18 17:09 DethAriel

To not keep you folks in the dark, here's a small status update.

Why it happens?

This error happens due to the following scenario:

  1. When the component is destroyed, ng-recaptcha invokes the grecaptcha.reset(ID) method
  2. This results in recaptcha invoking the api2/anchor endpoint that @BrentACole mentions
  3. For some reason this endpoint times out
  4. Since this unhandled promise lies inside recaptcha__en.js file there's no way we could catch it (e.g. patching this file is not an option given such a sophisticated minimization algorithm used)

Why not just skip invoking grecaptcha.reset during ngOnDestroy?

It's there for a reason, and this reason is #10 .

What can I do?

If you can - ignore it. Add this error to an allowlist in your e2e tests or frontend monitoring software.

Otherwise, you can override the ngOnDestroy method of RecaptchaComponent manually (or create a component fork). Please be aware, that this is a temporary and pretty bad fix that might put you in a very unfortunate position when upgrading component versions! Nevertheless, here's approximate code for that:

// somewhere in your "main.ts"
import { RecaptchaComponent } from 'ng-recaptcha';
RecaptchaComponent.prototype.ngOnDestroy = function() {
  if (this.subscription) {
    this.subscription.unsubscribe();
  }
}

What next?

I've submitted this issue to the recaptcha support team, and I'll post an update here when I have it.

Thx for bearing with me on that!

DethAriel avatar Oct 02 '18 01:10 DethAriel

Hey @DethAriel, can you paste the link to the recapcha issue you created?

SirWojtek avatar Oct 08 '18 08:10 SirWojtek

@SirWojtek the recaptcha core team does not have a public bug tracker (at least not one that I know of). I've submitted an issue to their support team through email, and I followed up again today. First time they recommended me to submit a question to StackOverflow, which I did with no results so far.

Let's see if they get back with some hints/workarounds/timelines anytime soon. I would be very happy to bring you the good news today, but right now this is all I've got

DethAriel avatar Oct 08 '18 21:10 DethAriel

I had someone Ina discord give me something like this which works most of the time but sometimes will still throw errors.


@ViewChild('captcha') captcha: ElementRef;
@ViewChild('submit') submit: MatButton;

captchaConnection;

renderCaptcha() {
	if (!grecaptcha.render) {
		setTimeout(() => {
			grecaptcha.render(this.captcha.nativeElement, {
				callback: (e) => this.captchaCallback(e)
			});
		}, 2000)
	}
	grecaptcha.render(this.captcha.nativeElement, {
		callback: (e) => this.captchaCallback(e)
	});
}

ErraticFox avatar Oct 19 '18 20:10 ErraticFox

@ErraticFox it does not look like this is solving the issue, could you please elaborate on what you mean by "works most of the time"? These errors are thrown when recaptcha is removed from DOM, not when it's rendered

DethAriel avatar Oct 24 '18 22:10 DethAriel

Anyone solved this error ? I had the same error on angular 4 with recaptcha.

LuiisFernando avatar Nov 14 '18 13:11 LuiisFernando

Trying @DethAriel's workaround on Angular 7, I'm getting:

ERROR in src/main.ts(23,12): error TS2339: Property 'subscription' does not exist on type 'RecaptchaComponent'.

@DethAriel also writes

this is a temporary and pretty bad fix that might put you in a very unfortunate position when upgrading component versions

I am not sure what this means? If it is pretty bad, does it not actually fix the bug?

h2software avatar Nov 29 '18 06:11 h2software

hm, I tried overiding the destroy event as suggested in https://github.com/DethAriel/ng-recaptcha/issues/123#issuecomment-426112101

and doesn't seems to have any effect

fxck avatar Nov 29 '18 08:11 fxck

now with the @DethAriel https://github.com/DethAriel/ng-recaptcha/issues/123#issuecomment-426112101

throws: image

matudelatower avatar Feb 01 '19 16:02 matudelatower

@matudelatower seems to still work fine for me (see this example), but as I mentioned, this is not a fix, and my current suggestion is to ignore this error and add it to the allow-list in the frontend monitoring tools.

DethAriel avatar Feb 01 '19 22:02 DethAriel

If someone is facing this issue in ionic/cordova app, i solved it by setting in config.xml, this would also need cordova whitelist plugin

haisham avatar Jan 11 '20 09:01 haisham

Has anybody found a solution to avoid this?

LSzelecsenyi avatar Jan 28 '20 12:01 LSzelecsenyi

Hello anybody found a solution, now ? maybe we can load script outside angular ? or catch errors manual ?

Cobr3n avatar Feb 03 '20 11:02 Cobr3n

I am still testing, but this seemed to work for me.

import { FormControl, Validators } from '@angular/forms';
import { RecaptchaComponent } from 'ng-recaptcha';
public captchaEl: FormControl = new FormControl(null, Validators.required);
  ngOnInit(): void {
    RecaptchaComponent.prototype.ngOnDestroy = function() {
      if (this.subscription) {
        this.subscription.unsubscribe();
      }
    }

    this.captchaEl.reset();
  }

Then after the form is submitted, I do this.captchaEl.reset(); again. Still testing, but I am not seeing the error anymore.

corpulent avatar Mar 26 '20 17:03 corpulent

@DethAriel any other updates or responses from the reCaptcha team?

ErraticFox avatar Mar 27 '20 20:03 ErraticFox

Google sadly has a bad habit of not providing cleanup functions for their APIs. reset() is semantically wrong method anyway, there should be a destroy() call.

I have played around a lot with simplified version of the project, having static grecaptcha api loaded in index.html and a tiny component with grecaptcha.render in ngAfterViewInit and different cleanup experiments in ngOnDestroy. I have failed to get it working reliably. Creating and destroying the component multiple times either leaks memory, results an error or both.

PRR24 avatar Apr 23 '20 20:04 PRR24

To not keep you folks in the dark, here's a small status update.

Why it happens?

This error happens due to the following scenario:

  1. When the component is destroyed, ng-recaptcha invokes the grecaptcha.reset(ID) method
  2. This results in recaptcha invoking the api2/anchor endpoint that @BrentACole mentions
  3. For some reason this endpoint times out
  4. Since this unhandled promise lies inside recaptcha__en.js file there's no way we could catch it (e.g. patching this file is not an option given such a sophisticated minimization algorithm used)

Why not just skip invoking grecaptcha.reset during ngOnDestroy?

It's there for a reason, and this reason is #10 .

What can I do?

If you can - ignore it. Add a whitelist for this error in your e2e tests or frontend monitoring software.

Otherwise, you can override the ngOnDestroy method of RecaptchaComponent manually (or create a component fork). Please be aware, that this is a temporary and pretty bad fix that might put you in a very unfortunate position when upgrading component versions! Nevertheless, here's approximate code for that:

// somewhere in your "main.ts"
import { RecaptchaComponent } from 'ng-recaptcha';
RecaptchaComponent.prototype.ngOnDestroy = function() {
  if (this.subscription) {
    this.subscription.unsubscribe();
  }
}

What next?

I've submitted this issue to the recaptcha support team, and I'll post an update here when I have it.

Thx for bearing with me on that!

Hi! Any news from google

RonRofe avatar Apr 24 '20 08:04 RonRofe

Hey @DethAriel have you tried moving the offending logic outside a zone?

constructor(private ngZone: NgZone) {
}

...

this.ngZone.runOutsideAngular(() => {
   // reCAPTCHA logic here
})

https://angular.io/api/core/NgZone#runoutsideangular

Just a thought!

johngrimsey avatar Apr 28 '20 14:04 johngrimsey

@johngrimsey No, it does not help.

PRR24 avatar Apr 28 '20 17:04 PRR24

Ah well. Still, great lib man.

johngrimsey avatar Apr 28 '20 17:04 johngrimsey

Could this be of some help? I'm on mobile so it's difficult to check, but thought I'd share so someone else could try this. Worth a shot. https://github.com/google/recaptcha/issues/269#issuecomment-606838861

ErraticFox avatar Apr 28 '20 18:04 ErraticFox

anyone found a fix for it? I'm still seeing this in version 5

gsinghkular avatar Aug 21 '20 15:08 gsinghkular

As a temporary solution, I use a RouteReuseStrategy. This allows you not to destroy the page (route) where the captcha is used, but to save it to the cache. As a result, the ngOnDestroy method for this component is never called and no error is thrown. The downside of this solution is that the component remains in memory, but in my case it is a small page with a feedback form, so I decided simply ignore it.

mprojs avatar Sep 18 '20 10:09 mprojs

Just adding to this bug. I'm getting the same error using the latest version of ng-recaptcha, but the issue now points to zone.evergreen.js. The original solution provided using main.ts also seems to have no effect. Going to keep working on this issue, but just wanted others to know it can also appear as zone.evergreen.js

image

nicholasconfer avatar Oct 06 '20 15:10 nicholasconfer

Just adding to this bug. I'm getting the same error using the latest version of ng-recaptcha, but the issue now points to zone.evergreen.js. The original solution provided using main.ts also seems to have no effect. Going to keep working on this issue, but just wanted others to know it can also appear as zone.evergreen.js

image

I am also experiencing this! :(

Cassidie avatar Oct 12 '20 18:10 Cassidie