Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
JavaScript

JavaScript: Why structuredClone Throws on Data That JSON.stringify Handled Just Fine

- 27.09.26 - ErcanOPAK

⚡ The ‘Safer’ Clone That Started Throwing Where JSON Never Complained

Swapping JSON.parse(JSON.stringify(obj)) for the newer, purpose-built structuredClone() is a very reasonable upgrade – it correctly clones Dates, Maps, Sets, and typed arrays that the JSON round-trip quietly mangles. But structuredClone is also genuinely STRICTER: where JSON.stringify silently drops functions, undefined values, and symbols (or throws only on a circular reference), structuredClone throws a DataCloneError the instant it encounters a function, a DOM node, or anything else it fundamentally cannot serialize – turning data that used to survive the old approach (minus the parts it silently dropped) into a hard runtime error instead.

🔎 The Problem

const config = {
  retries: 3,
  onError: (err) => console.error(err),  // A function property
  createdAt: new Date(),
};

// Old approach: "worked" - silently dropped onError entirely, and
// turned createdAt into a plain ISO string instead of a real Date.
const oldClone = JSON.parse(JSON.stringify(config));
console.log(oldClone.onError);      // undefined - just quietly gone
console.log(oldClone.createdAt);    // a string, not a Date anymore

// New approach: throws immediately instead of silently dropping onError.
const newClone = structuredClone(config);
// Uncaught DOMException: Failed to execute '"'"'structuredClone'"'"' -
// (err) => ... could not be cloned.

✅ Fix: Separate What Actually Needs Cloning From What Doesn’t

  • Functions, class instances with methods, and DOM references were never genuinely being ‘cloned’ by the JSON approach – they were being silently discarded, so the honest fix is to exclude them from the object being cloned in the first place (clone only the plain data, and reattach any function/behavior separately afterward), rather than looking for a clone function that tolerates them.
  • structuredClone’s error is specific about what it couldn’t clone, which makes it a genuinely useful signal during a migration away from the JSON round-trip – each DataCloneError it throws is pointing at a spot where the old code was silently losing data that nobody previously noticed was missing.
  • For an object that intentionally mixes data with behavior (a class instance with both properties and methods), a dedicated clone() method on the class itself – copying only the fields that matter and reconstructing the rest – is more correct than reaching for either generic clone approach, since neither one actually understands what ‘a correct copy of this specific type’ means.

⚠️ Why the Old Code ‘Worked’ at All

  • JSON.stringify’s behavior toward things it can’t represent (silently omitting the property entirely, for a function or undefined; throwing only for a genuine circular reference) was never actually correct – it just failed in a way that didn’t throw, which is a very different thing from actually succeeding.
  • A codebase that’s relied on the JSON round-trick for cloning for a long time can have quite a few objects where ‘the clone lost a property’ was already happening silently and nobody noticed, specifically because nothing downstream ever needed that particular property from the cloned copy – switching to structuredClone is often the first time this becomes visible at all.

JSON.stringify never actually cloned everything – it just failed quietly on the parts it couldn’t handle, and structuredClone is simply the first version of this idea honest enough to say so out loud.

— JavaScript Tip

Related posts:

JavaScript: Use ES6 Modules for Organized Code

Send JSON the Right Way

Getting Session value in Javascript & Asp.Net

Post Views: 1

Post navigation

The AI Prompt That Turns a Lease Renewal Offer Into Terms Worth Pushing Back On
Windows 11: Fix Widgets That Keep Reopening on Startup After You Disable Them

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Sep    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (939)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (624)
  • Add Constraint to SQL Table to ensure email contains @ (590)
  • Average of all values in a column that are not zero in SQL (553)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (526)
  • Find numbers with more than two decimal places in SQL (468)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps
  • Photoshop: Fix a Color Profile Mismatch That Makes Printed Output Look Nothing Like What You Saw On Screen
  • WordPress: Fix Search Results That Return Pages From a Theme You Deactivated Months Ago
  • Visual Studio: Fix a Test Project That Builds Fine Alone but Fails to Discover Any Tests After a NuGet Restore
  • ASP.NET Core: Fix a File Upload That Times Out on Slow Connections Only Because Kestrel’s Minimum Data Rate Feature Kicked In
  • JavaScript: Fix an Array Destructuring Default Value That Silently Never Applies Because null Was Passed Instead of Undefined

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (973)
  • How to make theater mode the default for Youtube (950)
  • How to add default value for Entity Framework migrations for DateTime and Bool (939)
  • Get the First and Last Word from a String or Sentence in SQL (847)
  • How to select distinct rows in a datatable in C# (836)

Recent Posts

  • CSS: Fix a prefers-color-scheme Media Query That Gets Silently Overridden by a Browser Extension’s Forced Dark Mode
  • Git: Fix a Merge Commit That Silently Drops a File Because Both Branches Deleted It Differently
  • HTML5: Fix a Native Lazy-Loading Image That Never Loads Because It Sits Inside a Hidden Tab Until the User Clicks It
  • The AI Prompt That Traces a Null Reference Exception Back to the Exact Line That First Produced the Null
  • The AI Prompt That Turns a Gym Membership Contract’s Fine Print Into a Plain-English List of Cancellation Steps

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com