Error an object could not be cloned

Preflight Checklist I have read the Contributing Guidelines for this project. I agree to follow the Code of Conduct that this project adheres to. I have searched the issue tracker for an issue that...

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.

Already on GitHub?
Sign in
to your account


Closed

geekduck opened this issue

May 22, 2020

· 12 comments

Comments

@geekduck

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for an issue that matches the one I want to file, without success.

Issue Details

  • Electron Version:
    • v9.0.0
  • Operating System:
    • macOS 10.15.4
  • Last Known Working Electron version:
    • v8.3.0

Expected Behavior

executeJavaScript doesn’t throw an error.

Actual Behavior

executeJavaScript throw an error.

$ npm start
> electron-quick-start@1.0.0 start /PATH/TO/electron-quick-start
> electron .

(node:63766) UnhandledPromiseRejectionWarning: Error: An object could not be cloned.
    at EventEmitter../lib/renderer/ipc-renderer-internal.ts.exports.ipcRendererInternal.send (electron/js2c/renderer_init.js:2324:20)
    at EventEmitter.<anonymous> (electron/js2c/renderer_init.js:2286:26)
(node:63766) UnhandledPromiseRejectionWarning: Error: An object could not be cloned.
    at EventEmitter../lib/renderer/ipc-renderer-internal.ts.exports.ipcRendererInternal.send (electron/js2c/renderer_init.js:2324:20)
    at EventEmitter.<anonymous> (electron/js2c/renderer_init.js:2286:26)
(node:63766) 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(). (rejection id: 1)
(node:63766) 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(). (rejection id: 1)
(node:63766) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:63766) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

To Reproduce

https://github.com/geekduck/electron-bug-report/commit/99bfc2489b5d4435084f20309c9257bb6ae9d26c

index.html

<!DOCTYPE html>
<html>
  <body>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
  </body>
</html>

main.js

  mainWindow.loadFile('index.html')
  mainWindow.webContents.executeJavaScript(`
      $(function() {
          console.log(123);
      });
  `);
$ git clone https://github.com/geekduck/electron-bug-report -b master
$ npm install
$ npm start

Additional Information

workaround

  mainWindow.loadFile('index.html')
  mainWindow.webContents.executeJavaScript(`
    (function($) {
      $(function() {
          console.log(123);
      });
    })(window.$);
  `);

@ChALkeR

Add ;0 to the end of the original script, otherwise the resulting value is attempted to be cloned and used as a result of executeJavaScript.

That’s basically why the work-around above helps – it hides the resulting value which comes from jQuery.

@vladimiry

Expected to happen as Structured Clone Algorithm is used for IPC and the returned stuff/jquery-wrapper is not serializable.

@codebytere

Closing as the above commentary are correct!

@geekduck

@fpsantos86

I’m having the same error with a component VFileInput and Electron:
-> Error in v-on handler: «Error: An object could not be cloned.»

@pimentaro

I’m having the same error with a component VFileInput and Electron:
-> Error in v-on handler: «Error: An object could not be cloned.»

me too

ericmandel

added a commit
to ericmandel/js9
that referenced
this issue

Aug 19, 2020

@ericmandel

  -- add work-around to avoid 'object could notbe cloned' error when
     running webContents.executeJavaScript() routine
  -- see: electron/electron#23722

@appurist

For anyone googling this error, this can also happen if a formerly synchronous function is suddenly made asynchronous, without the corresponding await in the caller. If the result was then sent via IPC, the serializer may not be able to handle this.

This isn’t a problem with Electron and the IPC, it’s effectively the missing await before the IPC.

I ran into this with the undocumented breaking changes made in electron-settings in its 4.0 major update (e.g. get and set are now async). I suspect the IPC is having trouble serializing a promise…

@camhart

For anyone else running into this, I had this occur due to simply importing electron-log on a renderer process

@somebeaver

For anyone googling this error, this can also happen if a formerly synchronous function is suddenly made asynchronous, without the corresponding await in the caller. […] I suspect the IPC is having trouble serializing a promise…

It’s simpler than that. Forgetting an await in your code, at any point, means your variable will be equal to the Promise itself instead of the results of the promise. Sending that Promise over IPC in Electron 8 and later will throw the error.

@m4heshd

Electron is awesome but this is one of the most awful things about being multi-processed. Hope deno would arrive at a better approach.

@roryok

IPC wasnt involved in my case. I’m running a vue app in electron. I have a computed returning an array of menu items, which are then rendered. Clicking on each one invokes a generic handler which is passed the item as a value

    <button
        v-for="item in menu" :key="name"
        @click="doClick(item)">
    </button>
    computed: {
        menu() {
            return [
                { name: 'stats', action: () => { alert('hi') } },
                { name: 'about' },
            ];
        }
    },
    methods: {
        doClick(item) {
            if (item.action) {
                item.action();            
            } else {
                // handle generic click
            }
        },
    }

If the item contains an action, I get the error [Vue warn]: Error in v-on handler: "Error: Uncaught Error: An object could not be cloned."

if I just did this it would work:

    <button
        v-for="item in menu" :key="name"
        @click="item.action">
    </button>

But I wanted to be able to handle items with no action too.

Didnt find a solution in the end. I tried everything I could think of including destructuring the item before passing in, which I was sure would work.

@click="doClick({ ...item })"

but it didnt. In the end I just had to refactor my code

@deleoeduardo

IPC wasnt involved in my case. I’m running a vue app in electron. I have a computed returning an array of menu items, which are then rendered. Clicking on each one invokes a generic handler which is passed the item as a value

    <button
        v-for="item in menu" :key="name"
        @click="doClick(item)">
    </button>
    computed: {
        menu() {
            return [
                { name: 'stats', action: () => { alert('hi') } },
                { name: 'about' },
            ];
        }
    },
    methods: {
        doClick(item) {
            if (item.action) {
                item.action();            
            } else {
                // handle generic click
            }
        },
    }

If the item contains an action, I get the error [Vue warn]: Error in v-on handler: "Error: Uncaught Error: An object could not be cloned."

if I just did this it would work:

    <button
        v-for="item in menu" :key="name"
        @click="item.action">
    </button>

But I wanted to be able to handle items with no action too.

Didnt find a solution in the end. I tried everything I could think of including destructuring the item before passing in, which I was sure would work.

@click="doClick({ ...item })"

but it didnt. In the end I just had to refactor my code

Try to fetch an array of items with the actions and clone the array items to show them like a menu. When you access to the menu, call a function that finds the original item and execute the action binded to it. You should have some items for showing like a menu and another array of items with the actions. Use the item key to find them in the actions array.
Hope this work for you, if you’d found a better solution, please post it here!

I am unable to pass any object or arrays to IPCRenderer.

I am getting error when passing an object or array through ipcs, I have even tried to send by converting to string using JSON.stringify but it converts it into empty object string.

I have tried passing a fileList, an array of object & even an object nothing passes. only string or handwritten objects are working.

I’ve read that it uses Structured Clone Algorithm and fileList & Array is allowed by this algorithm

ERROR:

electron/js2c/renderer_init.js:74 Uncaught Error: An object could not be cloned.
    at EventEmitter.i.send.i.send (electron/js2c/renderer_init.js:74)
    at HTMLButtonElement.compressNow (ImageHandling.js:190)

ElectronJS ipcRenderer Error An object could not be cloned.

I have tried many possible solutions but nothing worked

code:


const compressNow = () => {
    ipcRenderer.send("image:compress", filess).  ///This is the error.
    // filess is a variable containing an array of selected files from an HTML input.
}

Now i have tried to send filess as JSON.stringify, i tried to send it as an object but nothing works unless i manually write a dummy object or string.

Here’s My Github Repo for this project

Files With ErrorJ:-

ImageHandling.js

const fs = window.require('fs');
const {ipcRenderer} = require("electron")
const SELECT = (target) => document.querySelector(`${target}`)
var filess = []

const imgUploadInput = SELECT("#imgUploadInput")
const warning = SELECT("#warning")

const setImgBase64 = (imgEl, file) => {

    const ReadAbleFile = fs.readFileSync(file.path).toString('base64')
    let src = "data:image/png;base64," + ReadAbleFile

    imgEl.setAttribute("src", src)
    // El.src=src

    // console.log(`FIXED IMAGE # ${imgEl} `,ReadAbleFile)

}
const renderImages = () => {
    const files = filess && Array.from(filess)
    const defaultImg = SELECT("#defaultImg")
    const addImgBtn = SELECT("#addImgBtn")
    imgUploadInput.disabled = true;

    let numOfFiles = files.length

    if (numOfFiles < 1) {
        SELECT("#compressContainer").style.visibility = "hidden"
    } else {
        SELECT("#compressContainer").style.visibility = "visible"
    }
    if (numOfFiles > 49) {
        warning.innerHTML = `<b style="font-weight:bold; color:red;">WARNING:</b><br/> 
                               <span style="padding:10px;text-align:left">
                               Your processor/computer may not be able to process ${numOfFiles} Images at once, We recommend selecting less than 50 Images at once for better performance.
                                </span>
                                `;
    }
    addImgBtn.innerHTML = `LOADING.....`
    if (defaultImg && numOfFiles > 0) 
        defaultImg.remove();
    


    setTimeout(() => {

        if (files && numOfFiles > 0) {
            let displayImages = SELECT("#displayImages")
            displayImages.innerHTML = ""
            files ?. forEach((file, i) => {
                let divEl = document.createElement("div")
                let imgEl = document.createElement("img")
                imgEl.src = file.path

                imgEl.id = `PNG_${i}_${
                    btoa(file.name)
                }`
                divEl.className = "displayedImg"

                imgEl.setAttribute("onclick", `document.getElementById('ImageView').src=this.src`)


                const a = document.createElement("a")
                a.appendChild(imgEl)

                a.setAttribute("href", `#ViewImage`)
                a.className = "perfundo__link"


                divEl.appendChild(a)

                divEl.className = "displayedImg perfundo"

                displayImages.appendChild(divEl)


                if (i == files.length - 1) {
                    warning.innerHTML = "";
                    updateNumOfImages();
                }
                imgEl.onerror = () => setImgBase64(imgEl, file) // converting to base64 only on error, this make performance better and help us avoid freezes. (before this i was converting all images to base64 wither errored or not that was making computer freez)
            })
            addImgBtn.innerHTML = "+ Add MORE"
            imgUploadInput.disabled = false
            findDuplicate()
        }

    }, 0);
}

const hasDuplicate=()=>{
    let FileNames = [... filess.map(f => f.name)]
    let duplicateFiles = filess.filter((file, i) => FileNames.indexOf(file.name) !== i)

    return {FileNames,duplicateFiles,FilesLength:duplicateFiles.length}
}
const findDuplicate = (forceAlert = false) => {
    if (filess && filess.length) {
        let {FileNames} = hasDuplicate()
        let {duplicateFiles} = hasDuplicate()
        if (duplicateFiles.length) { // alert(``)

            let countFiles = duplicateFiles.length
            let fileStr = countFiles > 1 ? "files" : "file"
            console.log("result from removeDup=> ", filess, " n dupfilename=> ", FileNames, " n dupfiles=> ", duplicateFiles)

            let shouldNotAsk = localStorage.getItem("NeverAsk")
            let msg = `You've selected ${
                countFiles > 1 ? countFiles : "a"
            } duplicate ${fileStr}`
            let duplInner = `<span style='color:red'> 
                               <b>WARNING</b>
                               <p style="margin:0px;line-height:1">  ${msg} .  <button onClick="findDuplicate(true)" type="button"  class="btn btn-danger btn-rounded  btn-sm">REMOVE DUPLICATE</button></p>
                              </span>`
            if (! shouldNotAsk || forceAlert) {
                swal("DUPLICATE FILES DETECTED", `${msg} , Would you like to un-select duplicate ${fileStr} having same name?`, {
                    icon: 'warning',
                    dangerMode: true,
                    buttons: {
                        cancel: true,
                        ...forceAlert ? {} : {
                            never: "Never Ask"
                        },
                        confirm: "Yes !"
                    }
                }).then((Yes) => {
                    if (Yes == "never") {
                        localStorage.setItem("NeverAsk", true)
                        warning.innerHTML=duplInner

                    } else if (Yes) {
                        removeDuplicates()

                    }
                })
            } else {
                warning.innerHTML=duplInner
            }
        }

    }
}


const removeDuplicates = (showAlert=true) => {
    
    let {FileNames} = hasDuplicate()
    let {duplicateFiles} = hasDuplicate()
    let duplicateFileNames = duplicateFiles.map(f => f.name)
    let uniqueFiles = filess.filter((file) => ! duplicateFileNames.includes(file.name))
    filess = [
        ... uniqueFiles,
        ... duplicateFiles
    ]

    console.log("result from removeDup=> ", filess, " n filename=> ", FileNames, " n dupfiles=> ", duplicateFiles, "n unique fil=> ", uniqueFiles)
    renderImages()
    if(showAlert){
    swal("DONE", "Removed Duplicate Files ", {icon: 'success'}).then(() =>{ 
        renderImages()
        setTimeout(() => {
             let hasDuplicateFiles = hasDuplicate().FilesLength
             if(hasDuplicate){//Re-check if any duplicate files left after the current removal process. 
                 removeDuplicates(false) //Re-run the function to remove remaining. false will make sure that this alert does not show and the loop does not continue.
             }
             renderImages()

        }, 10);
    
    })
   }
}




const updateNumOfImages = () => {
    warning.innerHTML = `
                <span style="text-align:left; color:green">
                        Selected ${
        filess.length
    } Image(s)
                 </span>
                 `;
}


const compressNow = () => {
    ipcRenderer.send("image:compress", filess)
    // alert("WOW")
}


CompressBtn.addEventListener("click", compressNow)

imgUploadInput.addEventListener("change", (e) => {
    let SelectedFiles = e.target.files

    if (SelectedFiles && SelectedFiles.length) {
        filess = [
            ... filess,
            ... SelectedFiles
        ]
        renderImages()
    }
})
// SELECT("#imgUploadInput").addEventListener("drop",(e)=>console.log("DROP=> ",e))

UPDATE:-

I REPLACED THIS:

const compressNow = () => {

        ipcRenderer.send("image:compress",filess)
    
}

INTO THIS:-

const compressNow = () => {

    filess.forEach(file => {
        ipcRenderer.send("image:compress",file.path )
    });
}

Now here i am sending the files one by one via forEach, actually its sending string «file path» so thats how its working i am still confused why do i have to do this? why can’t i send whole fileList i assume that this loop method is a bad practice because it will consume more CPU its one additional loop however it won’t be necessary if i am able to send the whole array.

When I try to sign a message with this simple function const signatue = await window.web3.eth.sign(hash, userAddress) I get an error.

In firefox I get Unhandled promise rejection DOMException: "The object could not be cloned."

The actual data I put into this function for example are: hash 0x2339863461be3f2dbbc5f995c5bf6953ee73f6437f37b0b44de4e67088bcd4c2 account_address 0x627306090abab3a6e1400e9345bc60c78a8bef57

I am using «web3»: «^1.0.0-beta.47» and Metamask 6.1.0.

In «1.0.0-beta.46» Iam getting error I have already mentioned there Cannot sign a message with web3js (invalid from address)

In beta «^1.0.0-beta.37» everything works fine.

Please help me :(

The same error in chrome

              if (!error) {
                resolve(response);
              }
   ...<omitted>... } could not be cloned.
    at i._write (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:114976)
    at b (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:133822)
    at chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:136934
    at i.g.write (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:136961)
    at e.exports.y (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:126211)
    at chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:37123
    at e.exports.s.emit (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:37193)
    at w (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:121050)
    at _ (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:120865)
    at e.exports.b.push (chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/inpage.js:1:121676)```



I’m trying to ask the main application to find some kind of device, I have been trying to do it with ipc but couldn’t make it work either with asynchronous send and sync send. I suspect that the main is trying to reply the promise.

Renderer:

const recognizedDevices = ipcRenderer.sendSync('findDevice');
console.log(recognizedDevices);

Main:

ipcMain.on('findDevice', (event) => findDevice(event));
const findDevice = async (event) => {
  let recognizedDevices = await findConnectedDevices();
  if(recognizedDevices){
  console.log("found");
  console.log(recognizedDevices);
  return event.returnValue = recognizedDevices;
  }
//TODO: If no device found
}

Result in main:

    found
[
  HID {
    _events: [Object: null prototype] { newListener: [Function (anonymous)] },
    _eventsCount: 1,
    _maxListeners: undefined,
    _raw: HID {},
    write: [Function: bound write],
    getFeatureReport: [Function: bound getFeatureReport],
    sendFeatureReport: [Function: bound sendFeatureReport],
    setNonBlocking: [Function: bound setNonBlocking],
    readSync: [Function: bound readSync],
    readTimeout: [Function: bound readTimeout],
    getDeviceInfo: [Function: bound getDeviceInfo],
    _paused: true,
    [Symbol(kCapture)]: false
  }
]

I was hoping to receive the same log result in the renderer, but unfortunately I am getting back Error: An object could not be cloned.

If I try to reply back with recognizedDevice.length I will indeed receieve «1» in the front end, so it seem like the communication between them is good. Seems like the problem is with sending the object.

iamohd-zz opened this issue 2 years ago · comments

Versions:

  • @inertiajs/inertia version: 0.9.2
  • @inertiajs/inertia-vue3 version: 0.4.7

Describe the problem:

I am using Vue 3 Draggable package. In the event of dragging an element, I execute Inertia.put method and pass some data to Laravel. This is where this exception occurs.

Steps to reproduce:

For example, in Laravel pass a project with a list of columns to your view. Then use the Draggable component as following:

<Draggable
    v-model="project.columns"
    group="columns"
    item-key="id"
    @end="onColumnPositionChanged"
>
    <template #item="{element: column}">
        <KanbanColumn
             :column="column"
             :key="column.id"
         />
    </template>
</Draggable>

In event of onColumnPositionChanged, trigger Inertia.put method

const onCardPositionChanged = () => {
    Inertia.put('/some-route');
};

I suspect this has something to do with the URL you’re submitting to, which I assume isn’t /some-route. What’s the exact URL that’s being used when submitting the request?

I think the issue is because of trying to push a complex object into the page state. Once I replaced the page state with simple data, it worked without any issue.

So, how are you getting complex data into your page props? We actually intentionally removed the transformProps option because of this very issue. The data that is saved to history state should just be the props (JSON) that come back from the server.

Also, can you please provide an example of what specifically you’re doing to cause this? I don’t see how the draggable package/component should have any bearing on what ends up in history state.

At first, I thought it’s because of Vue Draggable, but it has nothing to do with this problem.

Actually, when the data that comes from the server contains nested arrays and you change this data in your view, then this issue occurs.

For example, suppose you are passing this to your view

return Inertia::render('back/projects/tasks/index', [
    'project' => [
        'id'  => $project->id,
        'name' => $project->name,
        'columns' => $project->columns()
              ->orderBy('index')
              ->get()
               ->transform(function (Column $column) {
                   return [
                       'id'    => $column->id,
                       'name'  => $column->name,
                       'index' => $column->index,
                       'cards' => [...]
                    ];
               })
         ]
    ]);

Now in your view, if you change the sort of columns and try to call Inertia.put, you will get the exception.

If it’s still not producible, I don’t mind giving you access to the repo to check how to produce the issue.

Gotcha. So, there is no reason why you shouldn’t be able to have a very complex/nested object structure like this. I do this all the time.

Can you identify specially what part of the data is causing the exception? You might have to systematically remove data until the problem goes away to figure this out. I’ve never run into an issue where server generated data caused an issue like this.

I tried to do that. It works when you replace ‘columns’ with an array of simple data types.

For example

But this exception occurs when you put a nested array

For example;

'columns' => [
    ['id'=>1], ['id'=>2]
],


In my case, it’s ‘cards’ that causing this issue since it’s an array that is inside ‘columns’

Are you sure there is not more to it? I just tried passing the following as props in my own demo app, and it worked without issue.

'columns' => [
    ['id'=>1], ['id'=>2]
],

Does this issue only happen when you make a visit via Inertia.put()? Or does it happen on the initial page load? If it’s only happening on Inertia.put(), can you confirm that you’re redirecting server-side back to the same page? Can you please show me the response that’s coming back from that request?

If you never change this data in your view then you will not get this exception.

Once you change it in your view and you call Inertia.put this will happen. Also, even when you try to navigate away after changing this data.

  • Yes in my controller I am redirecting back to the same page.
  • The request does not reach the server once this exception occurs, there is no response

Are you use the «remember» feature at all?

This is a strange issue. From what it sounds like, there is some type of page object data structure that’s causing this error. I am using the example data above that you’re saying is causing this issue, and I am unable to reproduce this. Basically, to fix this, I need to be able to reproduce it.

I don’t really want access to your full app. Much better is a very simple Laravel/Inertia/Vue3 app that reproduces this issue with just one or two endpoints, and one or two components. If you can provide that, I’ll happily look deeper into this. 👍

Great, I will make a repo to reproduce this issue and post the link here

Here it is:
https://github.com/iamohd/inertia-exception

To produce the exception, change the order of the columns by dragging them, and click on «Call Inertia.put» or «To another page» button

Also, you can try clicking on either of the buttons without changing the order of the columns, you will notice it will work fine in this case.

same problem
@inertiajs/inertia version: 0.9.2
@inertiajs/inertia-vue3 version: 0.4.7

i am using Howler.js plugin and vuex
an error occurs when navigating to another page via inertia-link

Uncaught DOMException: Failed to execute ‘replaceState’ on ‘History’: HTMLAudioElement object could not be cloned.

no problems in the old version:
@inertiajs/inertia version: 0.8.7
@inertiajs/inertia-vue3 version: 0.3.14

I was having the same issue

Uncaught (in promise) DOMException: The object could not be cloned. router.ts:397:4
    replaceState router.ts:398
    saveScrollPositions router.ts:73
    resetScrollPositions router.ts:91
    g router.ts:381
    (Async: promise callback)
    g router.ts:379
    (Async: promise callback)
    setPage router.ts:373
    handleInitialPageVisit router.ts:52
    init router.ts:41
    setup app.js:43
    callWithErrorHandling runtime-core.esm-bundler.js:155
    setupStatefulComponent runtime-core.esm-bundler.js:7161
    setupComponent runtime-core.esm-bundler.js:7117
    mountComponent runtime-core.esm-bundler.js:5115
    processComponent runtime-core.esm-bundler.js:5090
    patch runtime-core.esm-bundler.js:4684
    componentEffect runtime-core.esm-bundler.js:5227
    reactiveEffect reactivity.esm-bundler.js:42
    effect reactivity.esm-bundler.js:17
    setupRenderEffect runtime-core.esm-bundler.js:5173
    mountComponent runtime-core.esm-bundler.js:5132
    processComponent runtime-core.esm-bundler.js:5090
    patch runtime-core.esm-bundler.js:4684
    render2 runtime-core.esm-bundler.js:5810
    mount runtime-core.esm-bundler.js:4085
    mount runtime-dom.esm-bundler.js:1322
    <anonymous> app.js:60
    InnerModuleEvaluation self-hosted:2384
    evaluation self-hosted:2335

What I noticed is that the exception occurs when I mutate the property, having an array that comes from the backend and then passed it to the vue component as property:

 props: {
    accounts: Array,
  },

I had a computed property which used the accounts prop, and applied a filter and a map methods which then the map method mutated the original array.

The issue was gone when I cloned the accounts array using JSON.parse and JSON.stringify

JSON.parse(JSON.stringify(this.accounts))

Also read the MDN for pushState which has a size limit of 640K, but that wasn’t my case.
https://developer.mozilla.org/en-US/docs/Web/API/History_API/Working_with_the_History_API#the_pushstate_method

And supported types, in my case it was a simple array of objects, but can be the issue for @oueki https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types

Same error happening here, basically we have deep object ( chart ) that comes from «$page.props.charts.orders»

Payload Demo here : https://api.npoint.io/63d8cf8b37aee1247b5a/orders
Please keep in mind more data is being shared along.

When passed via Prop to the component like follows:

<chart :chart="$page.props.charts.orders" />

This will reproduce the error from OP, but if we do like @tonychuuy mentioned and have a prop as follows :

computed:{ ordersChart(){ return JSON.parse(JSON.stringify(this.$page.props.charts.orders)) } }

<chart :chart="ordersChart" />

It will work just fine, im not sure what the issue is, but seems like some deep objects are being converted to Proxy and so fails to replace the state on those.

After some time with the debugger i was able to spot the proxied object :

image

Versions

  • «vue»: «^3.1.4»,
  • «@inertiajs/inertia-vue3»: «^0.5.1»,
  • «@inertiajs/inertia»: «^0.10.0»,

Hi @jechazelle

As a temporary solution you can clone the prop into a new reactive variable and use it

Just make sure u truly clone it, not making a shallow copy.

@reinink @iamohd — I am having the same issue, did you find a fix?

@iamohd — can you show how to do the temporary solution in the mean time?

@reinink @iamohd — I am having the same issue, did you find a fix?

@iamohd — can you show how to do the temporary solution in the mean time?

For example

import { usePage } from '@inertiajs/inertia-vue3';
import { reactive } from 'vue';

const obj = reactive({ ... usePage().props.value.someObject });

Hey everyone… Could this maybe be related to a local storage max size?

I ran into this issue when I just stored the entire form like this:

// Threw above error when I updated specific fields in my form
remember: ['form'],

I solved it by removing the exceptionally large parts of the form (in my case, entire models with deeply nested props, related models, and image data):

// Works when I remove those specific problematic and large form props
remember: [
    'form.title',
    'form.label',
    // ... other small field types
],

Now it works great!

Well… I thought it was working…

I think the issue is because of trying to push a complex object into the page state. Once I replaced the page state with simple data, it worked without any issue.

So, how are you getting complex data into your page props? We actually intentionally removed the transformProps option because of this very issue. The data that is saved to history state should just be the props (JSON) that come back from the server.

This may be my issue too… I’m sending different data than I’m receiving…

I ran into the same problem and found another possible workaround:

For whatever reason it appears that if I mutate the array part of the prop with something like array.splice() instead of setting that value to a new array with something like array.filter(), it doesn’t seem to trip the error.

For example, let’s say I have a prop data structure like this:

  someProp: {
    someInt: 3,
    someArray: [
      {
        name: "someObject1",
        id: 1,
      },
      {
        name: "someObject2",
        id:2,
      }
    ],
    someString:  "string",
  },

..and I want to remove «someObject1» based on it’s id.

If I use someArray.filter(), like so, I trip the mysterious «Object could not be cloned» error:

let idToRemove = 1;
this.someProp.someArray = this.someProp.someArray.filter( arrayItem => {
  return arrayItem.id !== idToRemove;
});

If, however, I use someArray.findIndex() and someArray.splice(), I don’t trip the error.

let idToRemove = 1;
let indexToRemove = this.someProp.someArray.findIndex( arrayItem => {
  return arrayItem.id == idToRemove;
});
this.someProp.someArray.splice(indexToRemove, 1);

I’m not sure what layer of the JavaScript/Inertia/Vuejs stack is causing this odd behavior, but I hope this information helps to work around and/or fix the issue.

I ran into the same error, also with draggable and Vue3. The error occurred with nested objects and ONLY if they were already present in the initial state. If the object is built after initialization the error does not occur.

My workaround for the problem is to always initialize the form’s deep keys «empty» and populate them after mounting:

I have a content key in my form which is a deeply nested object and i always clone the content coming from a prop after mounting:

const form = useForm({
     content: {}
 })

onMounted(() => {
    form.content = JSON.parse(JSON.stringify(props.page.content));
});

in a computed property..
which adds bindings for v-bind
for when looping over dynamic components..

I did what Numenorian / jannescb suggested and the error went away

let filters = JSON.parse(JSON.stringify(this.filters));

return filters.map(() => {})

I pulled my hair on this one for months. My solution was similar to this #775 (comment) above

Spoiler : the problem was because a «Proxy» made its way to my state.

image

So I had to find out what couldn’t be cloned ?!

image

put a breakpoint there… and started to dig into the e object until i saw this

image

I tried to «unProxy» the value with JSON.parse(JSON.stringify(formField)), and this solved the issue.

Yep. I came across this today and turns out I had proxy object in my state! Thanks guys

<Draggable
    v-model="project.columns"
    group="columns"
    item-key="id"
    @end="onColumnPositionChanged"
>
    <template #item="{element: column}">
        <KanbanColumn
             :column="column"
             :key="column.id"
         />
    </template>
</Draggable>

Try changing v-model="project.columns" to :list="project.columns" i was having a similar issue and this seams to have solved it for me

Source: https://github.com/SortableJS/vue.draggable.next#list

Just to chime in that I’m also seeing this issue with Vue Draggable and Inertia.put.

JSON.parse(JSON.stringify(formField)) does seem to solve the issue.

Just to chime in that I’m also seeing this issue with Vue Draggable and Inertia.put.

JSON.parse(JSON.stringify(formField)) does seem to solve the issue.

Are you using v-model or list?

My understanding of the issue is when you modify a value that came as «props», event if it’s nested deep down in an object.

Are you using v-model or list?

v-model

Are you using v-model or list?

v-model

Try using :list to see if you dont need to json parse it

And let us know

I had the same error, I solved it by sending the complex JSON object as a string and JSON.parse it on the front end.

I’m getting this same error with my checkbox component.

<script setup>
import { computed } from 'vue'

const props = defineProps(['modelValue','input'])
const emit = defineEmits(['update:modelValue'])

const value = computed({
  get() {
    return props.modelValue
  },
  set(value) {
    emit('update:modelValue', value)
  }
})
</script>

<template>
  <div>
    <template v-for="option in input.options">
        <input type="checkbox" v-model="value" :name="input.id" :value="{ id: option.id, value: option.value }" />
        <label>{{ option.label }}</label>
    </template>
  </div>
</template>

This fixes it, but just seems dirty/unnecessary

-    emit('update:modelValue', value)
+    emit('update:modelValue', JSON.parse(JSON.stringify(value)))

I had the same problem, also with Vuedraggable. And yes, at first it seems a problem with Inertia (and maybe it is in some way), but the thing is that modifying data that came in as a prop, is never a good idea and you should not change a prop’s data (also see the Vue documentation).

What I did to solve the problem, is creating a clone of the prop that came in (processes in my case) and then pass that as v-model on the Vuedraggable component.

export default {
    ...
    props: {
        processes: Array,
    },
    data() {
        const processesClone = this.processes.map((item) => {
            return JSON.parse(JSON.stringify(item));
        });
        return {
            processesClone: processesClone,
        }
    },
    watch() {
        processesClone(processes) {
            this.$inertia.post( ... );
        },
    },
    ...
}
<draggable v-model="processesClone" tag="tbody">
    <template #item="{ element, index }">
        <tr>
            ...
        </tr>
    </template>
</draggable>

You could also add a watcher to change the clone whenever the prop changes.

I’m just going to pop this on here: https://web.dev/structured-clone/

It digs in to the new structuredClone() api and lists some of the shortcomings of the JSON.parse(JSON.stringify()) approach.

For anyone running into issues it might turn out to be a better option, or you might find you need a library like Lodash.

Hopefully this is helpful to anyone who has scrolled this far with no success!

@ewhicher I haven’t been able to make structuredClone work. I get this error.

"UnhandledPromiseRejectionWarning: DataCloneError: Proxy object could not be cloned."

But JSON.stringify seems to do the trick.

Edit: The error above war from Firefox. I just noticed the error in Chrome is different

Chrome:

Uncaught (in promise) DOMException: Failed to execute 'replaceState' on 'History': [object Array] could not be cloned.

structuredClone error:

DOMException: Failed to execute 'structuredClone' on 'Window': [object Array] could not be cloned.

I had the same issue when using usePage() inside of the state function of Pinia.

Before:

    state: () => {
        return {
            cart: usePage().props.value.cart as Cart,
        }
    },

    actions: {
        setQuantity(product_id: number, quantity: any) {
            // Here an update to the cart happened
            this.cart.products = this.cart.products.map(productInCart => {
                if (productInCart.product_id !== product_id) {
                    return productInCart;
                }

                return {
                    ...productInCart,
                    quantity: quantity,
                }
            })
            
            // Then this PUT request will error
            Inertia.put('/cart/set', {
                cart_id: this.cart.id,
                product_id,
                quantity,
            }, {
                preserveScroll: true,
                only: ['cart', 'errors'],
            });
        },
    },

I’ve fixed it by changing the state to this:

    state: () => {
        const cart =  JSON.parse(JSON.stringify(usePage().props.value.cart))

        return {
            cart: cart as Cart,
        }
    },

Passing props into ref instead of change the value of it

java.lang.CloneNotSupportedException throws when object’s class does not implement the cloneable interface and  clone method in class Object has been called to clone an object.
Some applications that override the clone method can also throw this exception to indicate that an object could not or should not be cloned.

Cloneable is marker interface which is not having any method to implement. When implement marker interface in class it indicate to JVM how to treat with this objects.
For more info about marker interface , it’s type and how to create check below link.

Marker Interface in Java, Use and Custom Marker Interface

clone() is protected method of object class which is super class of all the classes. if need to use clone() method have to override it .

Constructors :

  • CloneNotSupportedException()

Create a CloneNotSupportedException object with no detail message.

  • CloneNotSupportedException(String message)

Create a CloneNotSupportedException object with  exception detail message.

Example

In below example creating clone object of Employee class from object there is no any compile time error because we have override clone method of Object Class in Employee class but it will throw CloneNotSupportedException while execution because as per

Contract : if cloneable interface is implemented then only clone method will call otherwise  JVM will throw CloneNotSupportedException.

Here we are doing Shallow cloning of object. For more info on shallow and deep cloning follow below link.

Employee Class

public class Employee {
private String firstName;
private String lastName;
private int age;
private double salary;
private Address address;

public Employee(String firstName, String lastName, int age, double salary, Address address) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.salary = salary;
this.address = address;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}

@Override
public String toString() {
return "Employee [firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + ", salary=" + salary
+ ", address=" + address + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}

}


Address Class

public class Address {
private String addressLine1;
private String city;
private String state;
private String contry;
private String pincode;

public Address(String addressLine1, String city, String state, String contry, String pincode) {
super();
this.addressLine1 = addressLine1;
this.city = city;
this.state = state;
this.contry = contry;
this.pincode = pincode;
}

public String getAddressLine1() {
return addressLine1;
}

public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getContry() {
return contry;
}

public void setContry(String contry) {
this.contry = contry;
}

public String getPincode() {
return pincode;
}

public void setPincode(String pincode) {
this.pincode = pincode;
}

@Override
public String toString() {
return "Address [addressLine1=" + addressLine1 + ", city=" + city + ", state=" + state + ", contry=" + contry
+ ", pincode=" + pincode + "]";
}

}


Test for ClassNotSupportedException

public class CloneExample {

public static void main(String[] args) {
Address address = new Address("Next to Metro Station", "Noida", "UP", "India", "201301");
Employee employee = new Employee("Saurabh", "Gupta", 30, 50000, address);
System.out.println("Employee Object Before Clone :");
System.out.println(employee);
try {
System.out.println("Employee Object After Clone :");
Employee employeeCopy = (Employee) employee.clone();

System.out.println(employeeCopy);
} catch (CloneNotSupportedException ex) {
ex.printStackTrace();
}

}

}


Output

Employee Object Before Clone :
Employee [firstName=Saurabh, lastName=Gupta, age=30, salary=50000.0, address=Add
ress [addressLine1=Next to Metro Station, city=Noida, state=UP, contry=India, pi
ncode=201301]]
Employee Object After Clone :
java.lang.CloneNotSupportedException: Employee
at java.lang.Object.clone(Native Method)
at Employee.clone(Employee.java:55)
at CloneExample.main(CloneExample.java:11)

Solutions :

To solve this CloneNotSupportedException we have to implement cloneable interface in Employee class as below and run this program again.

public class Employee implements Cloneable<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>{

}

Summary :

  • Explain about CloneNotSupportedException by example and how it happen.
  • Constructors of CloneNotSupportedException.
  • How to fix CloneNotSupportedException.
  • Cloneable Marker Interface.

References :

https://docs.oracle.com/javase/8/docs/api/java/lang/CloneNotSupportedException.html

Понравилась статья? Поделить с друзьями:
  • Error an error occurred while setting account details
  • Error an error occurred while saving the snapshot failed to quiesce the virtual machine
  • Error an error occurred when verifying security for the message
  • Error an error occurred in the upload please try again later
  • Error an error occurred in the secure channel support перевод