Uncaught (in promise) RangeError: Maximum call stack size exceeded firebase - firebase

I am simply trying to add a record to the database with a cloud function. For some reason, I am getting the above error. This doesn't make sense as I am not making any calls inside the method. At first, I thought it might have something to do with a return, but I tried every combination of return or not returning and nothing worked. Please help
Here is how I call the cloud function
function sendFriendRequest(userUid)
{
//userUid is user that will recieve request
var curUser = firebase.auth().currentUser;
userUid = userUid.substring(1);
var sendRequest = firebase.functions().httpsCallable('sendFriendRequest');
sendRequest({data: {sendingUser: curUser, recievingUser: userUid}}).then(function(result) {
//No return
});
}
Here is the cloud function
exports.sendFriendRequest = functions.https.onCall((data, context) => {
console.log("Made it to sendFriendRequest");
var requestedUserProfileRef = admin.database().ref("Users/" + data.data.recievingUser + "/FriendRequests");
requestedUserProfileRef.child(data.data.sendingUser.uid).set(data.data.sendingUser.displayName);
console.log("Finished sendFriendRequest");
});

I eventually figured this out and thought that I might as well share it with anyone who might need it in the future. So what was wrong was that I was passing in an object as the value for sendingUser. Apparently, you cant do that. Its a weird error and it doesn't seem to correspond to the actual error.

Related

Firebase Admin SDK Auth error "TOO_MANY_ATTEMPTS_TRY_LATER"

I'm using firebase admin sdk in my cloud functions and I'm getting error randomly in some executions when trying to get a user by uid .
let userRecord = await admin.auth().getUser(userId);
The error details are:
{"error":{"code":400,"message":"TOO_MANY_ATTEMPTS_TRY_LATER",
"errors":[{ "message":"TOO_MANY_ATTEMPTS_TRY_LATER",
"domain":"global","reason":"invalid"}]
}
}
My cloud function executes on a real time database write and can be triggered for multiple users. In total I have 4 auth function calls in one execution first is the above one, second call is to again get user by uid or email, third call is generateEmailVerificationLink and the last call is generatePasswordResetLink.
I have checked the rate limits in documentation for auth but there is no mention of rate limit for these operation. Also the error TOO_MANY_ATTEMPTS_TRY_LATER was only mentioned in REST API for sign up with email password.
If this error is due to rate limit what should I change to prevent this error given these 4 calls are necessary for the operation needed on database write?.
EDIT:
I have identified the actual call which is throwing too many attempts error. The calls auth().generateEmailVerificationLink() and auth().generatePasswordResetLink() throw this error when called too many times.
I called these two in loop with 100 iterations and waited for the promises. The first executions finishes without any errors i.e. 200 requests. But starting second execution as soon as the first one ends will throw the error of too many attempts. So I think these two calls have limit. Now I'm trying to reduce these calls and reuse the link information. Other calls like getUserByEmail works fine.
let promises = [];
let auth = admin.auth();
let hrstart = process.hrtime()
for (let i = 0; i < 100; i++) {
promises.push(auth.getUserByEmail("user email"));
promises.push(auth.generateEmailVerificationLink("user email", {url: `https://app.firebaseapp.com/path`}));
promises.push(auth.generatePasswordResetLink("user email", {url: `https://app.firebaseapp.com/path`}));
}
Promise.all(promises)
.then(value => {
let hrend = process.hrtime(hrstart);
console.log(hrend);
// console.log(value)
});
The error was specifically in the operation auth.createEmailLink. This function has following limit: 20QPS/I.P address where QPS is (query per second). This limit can be increased by submitting the use case to Firebase.
I got this information from firebase support after submitting my issue.
Link to my github issue: https://github.com/firebase/firebase-admin-node/issues/458
I was way under 20QPS but was receiving this exception. In fact, it would always throw the TOO_MANY_ATTEMPTS_TRY_LATER exception on the 2nd attempt.
It turned out to be usage of FirebaseAuth.DefaultInstance instead of instantiating a static instance thusly:
In class definition:
private readonly FirebaseApp _firebase;
In class constructor:
_firebase = FirebaseAdmin.FirebaseApp.Create();
In function:
var auth = FirebaseAuth.GetAuth(_firebase);
var actionCodeSettings = new ActionCodeSettings()
{
...
};
var link = await auth.GenerateEmailVerificationLinkAsync(email, actionCodeSettings);
return link;
In addition to the answer mentioned in https://stackoverflow.com/a/54782967/5515861, I want to add another solution if you found this issue while trying to create custom email verification.
Inspired by the response in this GitHub isssue https://github.com/firebase/firebase-admin-node/issues/458#issuecomment-933161448 .
I am also seeing this issue. I have not ran admin.auth().generateEmailVerificationLink in over 24hrs (from anywhere else or any user at all) and called it just now only one time (while deployed in the prod functions environment) and got this 400 TOO_MANY_ATTEMPTS_TRY_LATER error ...
But, the client did also call the Firebase.auth.currentUser.sendEmailVerification() method around same time (obviously different IP).
Could that be the issue?
My solution to this issue is by adding a retry. e.g.
exports.sendWelcomeEmail = functions.runWith({failurePolicy: true}).auth.user().onCreate(async (user) => {
functions.logger.log("Running email...");
const email = user.email;
const displayName = user.displayName;
const link = await auth.generateEmailVerificationLink(email, {
url: 'https://mpj.io',
});
await sendWelcomeEmail(email, displayName, link);
});
The .runWith({failurePolicy: true}) is key.
It s giving you an error because your cloud functions/backend call the generateEmailVerificationLink while at the same time the default behaviour of the Firebase is also doing the same and it is counted as 20QPS. It some weird Google Rate Limit accounting rule. So my solution is just to add a retry.
The Downside is, it is calling twice, so if the call is billable, it might be billable twice.

UnhandledPromiseRejectionWarning when using Fluture `encaseP` on `fetch`

I have just started using Flutures, and I am trying to fetch some remote data for a visualization with d3.
I created a function which accepts a DOM selector (e.g. #my-chart) and a url (e.g. https://example.com/data.json).
If an error occurs when fetching the data, I have a unary function that shows an error message. If everything goes well, I have a unary function that draws the visualization. For the sake of simplicity, let's suppose that these functions are just console.error and console.log.
const fn = async (selector, url) => {
// convert fetch (which returns a Promise) into a function that
returns a Future
const fetchf = Future.encaseP(fetch);
fetchf(url)
.chain(res => Future.tryP(_ => res.json()))
.fork(console.error, console.log);
}
Apparently I am missing something when wrapping fetch in a Future, because I get this warning:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().
If I had to use async/await I would write something like this, which would not give me any warning.
const fn = async (selector, url) => {
let res;
try {
res = await fetch(url);
} catch (err) {
console.error(err);
return;
}
let data;
try {
data = res.json();
} catch (err) {
console.error(err);
return;
}
console.log(data);
};
It seems two things are going on here:
The data.json() function is not supposed to be wrapped inside tryP, because according to your second not broken example, it returns synchronously (there is no await). This would cause Fluture to raise a TypeError (because it expects to see a Promise, but gets a JSON value). Although, knowing the fetch API, data.json() typically does return a Promise, so it might also be your second example is broken, and something else is going on. Whatever it is, I suspect that an unexpected Error is being thrown somewhere. Do you see any other error messages in your console, besides the one you posted?
I did some testing, and it does seems to be true - when Fluture raises or catches a TypeError after a successful encaseP, it seems the original Promise manages to catch that error, and trigger the unhandled rejection. This appears to be a regression bug in Fluture, and I will be fixing it soon. In the meantime, if we get to the bottom of what's throwing your error, you will be able to continue without depending on said fix.
EDIT: I've opened a PR to fix the second issue: https://github.com/fluture-js/Fluture/pull/310
EDIT2: The fix has been released under version 10.3.1. Using that version should give you more insights in what's happening with issue 1.

Firebase cloud functions stop observer from another function

First function observe any process. However, if first function will not send any response, i want to stop observer manually according to second function.When i call second function, observer does not stop in first function. How can i handle this situation ?
exports.startObserverFunc = functions.https.onRequest((req,res) => {
var userFirebaseID = req.query.firebaseID;
var ref = admin.database().ref("Quickplay");
let callback = ref.child(userFirebaseID).on("value",function(snapshot){
if (snapshot.exists()){
// Some Codes
ref.child(userFirebaseID).off("value", callback);
res.status(200).send({
resultName: "ok"
});
}
});
});
exports.stopObserverFunc = functions.https.onRequest((req,res) => {
var userFirebaseID = req.query.firebaseID;
var ref = admin.database().ref("Quickplay");
ref.child(userFirebaseID).off("value");
res.status(200).send({
resultName: "ok"
});
});
You should avoid using observers/listeners like this in Cloud Functions. It will likely not do what you want, given that your code could be running on any number of server instances, based on the load on your functions. Also, two function invocations are definitely not going to be running on the same server instances, so they have no knowledge of each other.
It's almost certainly the case that you just want to use once() to query for a single object just one time, and use that in your response. This is what all the official samples do.

Randomly occurs with Meteor: "TypeError: Cannot read property 'invalidate' of undefined"

On what seems to be random occasions, when a user on my website tries to answer a question on the quiz page, the following error occurs:
TypeError: Cannot read property 'invalidate' of undefined
at Tracker.Dependency.changed (tracker.js:388)
at ReactiveVar.set (reactive-var.js:82)
at null. (builtins.js:22)
at view.js:191
at Function.Template._withTemplateInstanceFunc (template.js:437)
at view.js:190
at Object.Blaze._withCurrentView (view.js:523)
at viewAutorun (view.js:189)
at Tracker.Computation._compute (tracker.js:294)
at Tracker.Computation._recompute (tracker.js:313)
I am unsure as to why this occurs, and I am not entirely sure when this error even means so if someone could point me in the right direction, that would be great!
Note: this error seems to only occur when using Chrome.
I'll hazard a guess, here's the culprit code in Tracker.Dependency
Tracker.Dependency.prototype.changed = function () {
var self = this;
for (var id in self._dependentsById)
// My Comment - we're expecting this to be a `Tracker.Computation` which has an `.invalidate()` method!
self._dependentsById[id].invalidate();
};
So, the problem is that something is modifying Object.prototype.
eg.
Object.prototype.test = function(){}
var emptyObj = {};
for (i in emptyObj)
console.log('has key=', i, 'isOwnProperty?=', emptyObj.hasOwnProperty(i))
will print out:
has key= test isOwnProperty?= false
A possible solution to try, would be adding this snipped to the client, and seeing if it fixes the problem (At least temporarily):
Tracker.Dependency.prototype.changed = function () {
var self = this;
for (var id in self._dependentsById){
if (self._dependentsById.hasOwnProperty(id)){
self._dependentsById[id].invalidate();
}
}
};
Tracker.Dependency.prototype.hasDependents = function () {
var self = this;
for(var id in self._dependentsById)
if (self._dependentsById.hasOwnProperty(id))
return true;
return false;
};
However - It's bad practice to add to Object.prototype, and lots of code will fail in unexpected ways. You'll need to track this down.
Hard to say without more info, but I'd take a look here, specifically the part under "Subscriptions Don't Block":
https://dweldon.silvrback.com/common-mistakes
As the author states, "In meteor, the majority of "Cannot read property of undefined"errors are caused by an incorrect assumption about the existence of subscribed data."
In other words, you may be trying to access the invalidate property before your subscriptions has returned anything.

angularFireCollection is not returning any data

I have no issues when using implicit updates (angelFire). However I need for some of my data use explicit updating. So I implemented angelFireCollection on the exact same ref I was using previously but despite the console.log explicitly saying that the read was granted and trying it with both with the onloadcallback and without, I don't get data directly into my assigned variable AND once the callback fires I get a strange looking object that DOES contain the data but not in the form I expect. My scope variable ends up with an empty collection. Never gets populated. Here is the code:
var streamController = function ($rootScope, $scope, $log, $location, angularFireCollection, profileService) {
//Wait for firebaseLogin...
$rootScope.$watch('firebaseAuth', init);
function init() {
if ($rootScope.firebaseAuth == false) {
return
};
var refUsers = new Firebase($rootScope.FBURL+'/users/'+$rootScope.uid);
$scope.profile = angularFireCollection(refUsers, function onload(snapshot) {
console.log(snapshot)
});
};
};
myApp.gwWebApp.controller('StreamController', ['$rootScope', '$scope', '$log', '$location', 'angularFireCollection', 'profileService',
streamController]);
}());
Here is what the console.log looks like ( ie; what snapshot looks like ):
>snapshot
T {z: R, bc: J, V: function, val: function, xd: function…}
Here is the earlier message before the snapshot was returned:
Firebase Login Succeeded! fbLoginController.js:16
FIREBASE: Attempt to read /users/529ccc5d1946a93656320b0a with auth={"username":"xxxxxxx#me.com","id":"529ccc5d1946a93656320b0a"} firebase.js:76
FIREBASE: /: "auth.username == 'admin'" firebase.js:76
FIREBASE: => false firebase.js:76
FIREBASE: /users firebase.js:76
FIREBASE: /users/529ccc5d1946a93656320b0a: "auth.id == $user" firebase.js:76
FIREBASE: => true firebase.js:76
FIREBASE:
FIREBASE: Read was allowed.
and finally the desired binding that ends up with an empty array: again from the console:
$scope.profile
[]
Anyone know what I could possibly be doing wrong?? This is like 5 lines of code. Frustrating.
I have put stops in angelFireCollection factory function and can see that the data is getting added to the collection in the callbacks inside that function but my binded variable never gets updated.
UPDATE
Ok experimenting with a plnkr. It seems that angularFireCollection EXPECTS your returning a LIST of items. The snapshot returns properly if you inspect snapshot.val() it will be whatever object structure was stored in firebase. IF you use angularFireCollection it does indeed bind to the variable HOWEVER it turns a non-list object into a garbled mess and you can not access the object user the normal dot operator. This is either a bug or it is a severe limitation of angularFireCollection which will cause me to revaluate how easily I can use firebase as the backend. I can't share my plnkr because it is accessing non-public data but tomorrow if i have time I will create a public firebase with an object store and demonstrate.
Ok. So it appears that indeed angularFireCollection is MEANT to be array based. Which is fine. It would be VERY helpful if the angularFire documentation was updated to make that clear. As such it is not an implicit vs explicit update technique.
For an explicit non-array based approach I have come up with the following code. Had I not been mislead by the documentation I would have gone down this path originally.
var MainCtrl = function($scope, angularFire) {
$scope.test = {};
var _url = 'https://golfwire.firebaseio.com/tmp';
var _ref = new Firebase(_url);
var promise = angularFire(_ref, $scope, 'implicit');
promise.then ( function(data){
$scope.explicit=angular.copy($scope.implicit );
});
}
You then work locally with the 'explicit' copy and when ready just update the 'implicit' by assigning: $scope.implicit = $scope.explicit.
Here is a plnkr: http://plnkr.co/edit/bLJrL1

Resources