Search code examples
node.jshttpserializationelectronipc

How can I fix IPC error "Error invoking remote method, an object could not be cloned" in Electron?


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.


Solution

  • 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;
      }
    );