16

I have a Cloud Function used to cross reference two lists and find values that match each other across the lists. The function seems to be working properly, however in the logs I keep seeing this Error serializing return value: TypeError: Converting circular structure to JSON . Here is the function...

exports.crossReferenceContacts = functions.database.ref('/cross-ref-contacts/{userId}').onWrite(event => {

    if (event.data.previous.exists()) {
        return null;
    }

    const userContacts = event.data.val();
    const completionRef = event.data.adminRef.root.child('completed-cross-ref').child(userId);
    const removalRef = event.data.ref;

    var contactsVerifiedOnDatabase ={};
    var matchedContacts= {};


    var verifiedNumsRef = event.data.adminRef.root.child('verified-phone-numbers');
    return verifiedNumsRef.once('value', function(snapshot) {

        contactsVerifiedOnDatabase = snapshot.val();

        for (key in userContacts) {
            //checks if a value for this key exists in `contactsVerifiedOnDatabase`
            //if key dioes exist then add the key:value pair to matchedContacts
        };

        removalRef.set(null); //remove the data at the node that triggered this onWrite function
        completionRef.set(matchedContacts); //write the new data to the completion-node

    });

});

I tried putting return in front of completionRef.set(matchedContacts); but that still gives me the error. Not sure what I am doing wrong and how to rid the error. Thanks for your help

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
MikeG
  • 3,745
  • 1
  • 29
  • 51

4 Answers4

16

I was having the exact same issue when returning multiple promises that were transactions on the Firebase database. At first I was calling:

return Promise.all(promises);

My promises object is an array that I'm using where I'm pushing all jobs that need to be executed by calling promises.push(<add job here>). I guess that this is an effective way of executing the jobs since now the jobs will run in parallel.

The cloud function worked but I was getting the exact same error you describe.

But, as Michael Bleigh suggested on his comment, adding then fixed the issue and I am no longer seeing that error:

return Promise.all(promises).then(() => {
  return true;
}).catch(er => {
  console.error('...', er);
});

If that doesn't fix your issue, maybe you need to convert your circular object to a JSON format. An example is written here, but I haven't tried that: https://stackoverflow.com/a/42950571/658323 (it's using the circular-json library).

UPDATE December 2017: It appears that in the newest Cloud Functions version, a cloud function will expect a return value (either a Promise or a value), so return; will cause the following error: Function returned undefined, expected Promise or value although the function will be executed. Therefore when you don't return a promise and you want the cloud function to finish, you can return a random value, e.g. return true;

steliosf
  • 3,669
  • 2
  • 31
  • 45
  • 1
    Welcome to programming - where doing normal things results in fascinating and unexpected ways. To be honest, how are we supposed to figure this kind of thing out in a sane way, rather than guess and check? FWIW, your answer fixed my issue (I was returning Promise.all([...]) with Firebase "jobs" – technoplato Aug 30 '19 at 04:14
  • 1
    Thanks this answer was helpful. I was using Promise.resolve(array_of_promises) by mistake after copying some code and doing this fixed the error: return Promise.all(allPromises).then(() => { return true; }); – Lucy Apr 20 '20 at 00:13
1

Try:

return verifiedNumsRef.once('value').then(function(snapshot) {
    contactsVerifiedOnDatabase = snapshot.val();

    for (key in userContacts) {
        //checks if a value for this key exists in `contactsVerifiedOnDatabase`
        //if key dioes exist then add the key:value pair to matchedContacts
    };

    return Promise.all([
      removalRef.set(null), //remove the data at the node that triggered this onWrite function
      completionRef.set(matchedContacts)
    ]).then(_ => true);
});
Michael Bleigh
  • 25,334
  • 2
  • 79
  • 85
  • Ok I will try this. Can you explain what `Promise.all([...])` is? – MikeG Jun 28 '17 at 01:58
  • 1
    @MikeG To learn about promises and how they work with Cloud Functions, check out this blog with embedded video: https://firebase.googleblog.com/2017/06/keep-your-promises-when-using-cloud.html – Doug Stevenson Jun 28 '17 at 03:06
  • 1
    @MichaelBleigh I still get the same error after trying this – MikeG Jun 28 '17 at 03:54
  • 5
    I am seeing exactly the same error in a similar use case. I am calling a transaction on a database reference after a chain of promises that earlier includes a once("value") read of the same reference. The function appears to have the desired effect, but I still get the error. – John Angland Jul 04 '17 at 14:06
  • I have the same problem @JohnAngland . Did u find the solution? the function works fine but I recieve that error all time – Francisco Durdin Garcia Jul 04 '17 at 21:55
  • 1
    I'm in the same club - returning a transaction gives me the same error, except the transaction processes correctly. – Ricky Jul 05 '17 at 00:57
  • @FranciscoDurdinGarcia I haven't found a way to avoid it in triggered functions. However, the problem does not occur when I do the same thing in using the web client. – John Angland Jul 05 '17 at 01:36
  • 2
    what if you do `return Promise.all([...]).then(function() { return true; });`? – Michael Bleigh Jul 05 '17 at 19:43
  • @FranciscoDurdinGarcia Michael's solution above worked for me. Try adding .then(result=>true) to your transaction. – John Angland Jul 07 '17 at 17:43
  • @JohnAngland this solution still does not solve the problem for me – MikeG Jul 10 '17 at 15:41
0

I had the same error output with a pretty similar setup and couldn't figure out how to get rid of this error. I'm not totally sure if every essence has been captured by the previous answers so I'm leaving you my solution, maybe it helps you.

Originally my code looked like this:

return emergencyNotificationInformation.once('value', (data) => {
    ...
    return;
});

But after adding then and catch the error did go away.

return emergencyNotificationInformation.once('value')
    .then((data) => {
        ...
        return;
    })
    .catch((error) => {
        ...
        return:
    });
}
Haves
  • 152
  • 1
  • 9
0

We fixed a similar issue with the same error by returning Promise.resolve() at the bottom of the chain, e.g.:

return event.data.ref.parent.child('subject').once('value')
    .then(snapshot => {
        console.log(snapshot.val());
        Promise.resolve();
    }).catch(error => {
        console.error(error.toString());
    });
UsAndRufus
  • 385
  • 5
  • 14
  • 1
    I don't have ownership of the project that I was experiencing this with at the moment. Can anyone else confirm this? – MikeG Sep 13 '18 at 15:27