Skip to content

DataCloneError when mutation variables contain Vue reactive proxies #181

@biesbjerg

Description

@biesbjerg

Description

When using TanStack Query with Vue 3, mutation variables are often Vue reactive() or ref() Proxy objects. The extension's mapMutationToData includes mutation.state.variables directly in the payload sent via postMessage to the content script. Since postMessage uses the structured clone algorithm, and Proxy objects are not structured-cloneable, this throws a DataCloneError on every mutation cache update.

The error is caught and logged, but it produces noisy console errors and prevents mutation data from appearing in the DevTools panel.

Console output

Error sending message to content script: DataCloneError: Failed to execute 'postMessage' on 'Window': [object Object] could not be cloned.
    at U (injected.js:20:28715)
    at ai.sendMutationDataUpdate (injected.js:20:31543)
    at injected.js:20:31216

Root cause

In src/injected/injected.ts, mapMutationToData reads mutation.state.variables and mutation.state.data as-is:

private mapMutationToData(mutation: Mutation): MutationData {
  return {
    mutationId: mutation.mutationId,
    state: mutation.state.status,
    variables: mutation.state.variables,  // may be a Vue Proxy
    data: mutation.state.data,            // may be a Vue Proxy
    // ...
  };
}

When passed to sendToContentScriptwindow.postMessage, the structured clone algorithm fails on Proxy objects.

This is not specific to Vue — any framework that wraps objects in Proxies (Vue 3, MobX, Solid stores, etc.) will hit this issue.

Suggested fix

Deep-serialize the mutation (and query) payloads before posting, for example:

function safeClone(value: unknown): unknown {
  try {
    return JSON.parse(JSON.stringify(value));
  } catch {
    return '[Unserializable]';
  }
}

private mapMutationToData(mutation: Mutation): MutationData {
  return {
    mutationId: mutation.mutationId,
    state: mutation.state.status,
    variables: safeClone(mutation.state.variables),
    data: safeClone(mutation.state.data),
    context: safeClone(mutation.state.context),
    error: mutation.state.error,
    submittedAt: mutation.state.submittedAt,
    isPending: mutation.state.status === "pending",
  };
}

JSON.parse(JSON.stringify(...)) naturally reads through Proxy getters, producing plain objects that are safe for structured cloning. The same approach could be applied to mapQueryToData for query.state.data to prevent similar issues with query data.

Environment

  • Extension version: latest from Chrome Web Store (Feb 2026)
  • Framework: Vue 3 with @tanstack/vue-query
  • Browser: Chrome

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions