4

The whole error message is the following:

Error: Error invoking remote method 'MY-IPC-CHANNEL': Error: An object could not be cloned. at EventEmitter.o.invoke (electron/js2c/renderer_init.js:71)

The electron/js2c/renderer_init.js:71 line is not my original line of code, but a compiled one.

I'm trying to send a POST request in order to get my Google access token, so that I can work with Google Drive's API. Currently I'm stuck trying to communicate between the renderer process and the main process by giving the main process the code I got from Google and making it send a POST request to the auth server. I have no problem establishing the connection but when I try to do it while sending an HTTP request I get the error above.

// ******* MAIN *******
function exchangeCodeForAccessToken(code: string) {
    const clientID = "My Google client ID";
    const clientSecret = "My Google client secret";
    const body = {
      code: code,
      client_id: clientID,
      client_secret: clientSecret,
      redirect_uri: "http://localhost:4000",
      grant_type: "authorization_code",
    };
    const body2 = `code=${code}&
    client_id=${clientID}&
    client_secret=${clientSecret}&
    grant_type=authorization_code`;

  // return fetch("https://oauth2.googleapis.com/token", {
  //   method: "POST",
  //   body: body
  // });

    return axios.post("https://oauth2.googleapis.com/token", body);
}

Here's the main handle:

// ******* MAIN *******
ipcMain.handle(
  OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
  async (event, code: string) => await exchangeCodeForAccessToken(code)
);

And the renderer invoke function:

// ******* RENDERER *******
function exchangeCodeForAccessToken(code: string) {
  ipcRenderer.invoke(OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL, code).then((response) => {
    console.log(response);
  }).catch((error) => {
        //TODO Improve error handling
        console.log(error);
    });
}

I tried sending the request through the net module from Electron. I also tried with the electron-fetch module, which is supposed to be an Electron integrated version of Node's fetch module. And finally I tried with the axios module, but it kept throwing the same error. I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up. Which means that the error is not only appearing when the promise is being returned but whenever the HTTP request function is being called. I also tried sending the request with both the object version of the request and its string version, hence the body and body2.

I don't know what I'm missing, and I'm so close to integrating Google login into my desktop app.

Lucas S. G.
  • 190
  • 1
  • 4
  • 16

1 Answers1

4

I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up.

It is an IPC error. You're returning the full response object, which presumably contains some properties with methods and/or other non-cloneable values. You need to make sure that the returned value can be cloned and sent to the renderer, for example:

ipcMain.handle(
  OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
  async (event, code) => {
    const response = await exchangeCodeForAccessToken(code);
    const {status, data} = response;
    return {status, data};
  }
);

I'm not sure how you called the function in your attempt to fix this, but I just ran this in Electron and it works without issues.

EDIT: Assuming response is coming from a fetch call (use response.json() if the data is JSON):

ipcMain.handle(
  OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
  async (event, code) => {
    const response = await exchangeCodeForAccessToken(code);
    const data = await response.text();
    return data;
  }
);
szydlovski
  • 800
  • 3
  • 10
  • You're a life saver! Thank you so much. – Lucas S. G. Jan 28 '21 at 16:35
  • do you by any chance have any tips on how to do the same thing with fetch instead of axios? The response type is a Response object, so I can't destructure it. – Lucas S. G. Jan 28 '21 at 17:41
  • 1
    Sure, you just need to call `text`, `json` or `arrayBuffer` on the response (depending on how you'd like to receive the data). That returns a promise, so you need to await it. I updated the anwer with an example. – szydlovski Jan 28 '21 at 18:09
  • @szydlovski, what are the conditions for the object to be cloneable? What are valid ways to return objects in this IPC return route? – Vass Sep 30 '21 at 02:20
  • @szydlovski, and how do you package multiple arguments before sending them to the handler? and how does they get received in the function header as parameters in the handler? – Vass Sep 30 '21 at 02:32
  • @Vass The object must be serializable to plain JSON, so it must not have any prototype modifications or non-serializable values. You can include multiple arguments as properties of a plain object. – szydlovski Oct 09 '21 at 15:28