{"record":{"id":"22b9ed353dc62402","repo":"dotnet/wpf","slug":"sr-freezable-attempttouseinnervaluewithdifferentthread","errorCode":null,"errorMessage":"SR.Freezable_AttemptToUseInnerValueWithDifferentThread","messagePattern":"SR\\.Freezable_AttemptToUseInnerValueWithDifferentThread","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Freezable.cs","lineNumber":1014,"sourceCode":"                    SetValue(dp, sourceValue);\n                }\n            }\n        }\n\n        // Throws if owner/child are not context free and on different dispatchers.\n        private static void EnsureConsistentDispatchers(DependencyObject owner, DependencyObject child)\n        {\n            Debug.Assert(owner != null && child != null,\n                \"Caller should guard against passing null owner/child.\");\n\n            // It is illegal to set a DependencyObject from one Dispatcher into a owner\n            // being serviced by a different Dispatcher (i.e., they need to be on\n            // the same thread or be context free (Dispatcher == null))\n            if (owner.Dispatcher != null &&\n                child.Dispatcher != null &&\n                owner.Dispatcher != child.Dispatcher)\n            {\n                throw new InvalidOperationException(\n                    SR.Freezable_AttemptToUseInnerValueWithDifferentThread);\n            }\n        }\n\n        // These methods provide an abstraction for managing Freezable context\n        // information - the context information being DO/DP pairs that the Freezable maps to.\n        //\n        // The methods will attempt to use as little memory as possible to store this information.\n        // When there is only one context it will store the information directly, otherwise it will\n        // place it within a list.  When using a list, these methods place the DO/DP pairs so\n        // that DOs are grouped together.  This is done so that when walking the graph, it\n        // is easier to track which DO's change handlers have already been gathered.\n        //\n\n        /// <summary>\n        /// Removes the context information for a Freezable.\n        /// <param name=\"context\">The DependencyObject to remove that references this Freezable.</param>\n        /// <param name=\"property\">The property of the DependencyObject this object maps to or null if none.</param>","sourceCodeStart":996,"sourceCodeEnd":1032,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/Freezable.cs#L996-L1032","documentation":"Freezable.EnsureConsistentDispatchers enforces that an owner Freezable and its child Freezable either live on the same Dispatcher (thread) or that one of them is context-free (Dispatcher == null). When both have non-null but different Dispatchers, attaching the child throws InvalidOperationException(SR.Freezable_AttemptToUseInnerValueWithDifferentThread), because the dependency graph would span threads that cannot invalidate each other.","triggerScenarios":"Assigning a frozen/created-on-another-thread Freezable (e.g. a Brush built on a background thread) as a property value or sub-object of a Freezable owned by the UI thread, e.g. via OnFreezablePropertyChanged during property setting or cloning.","commonSituations":"Building visuals/brushes on worker threads and handing them to UI-thread objects; mixing objects across threads when pre-rendering for performance; forgetting to freeze or to create the child on the consuming thread.","solutions":["Freeze the child Freezable before using it cross-thread (frozen objects are thread-safe and context-free)","Create the child object on the same Dispatcher/thread as the owner","Pass a Clone/CloneCurrentValue made on the owner's thread instead of the original instance","Check Dispatcher equality before assignment and marshal via Dispatcher.Invoke if needed"],"exampleFix":"// before\nuiElementBrush.GradientStops.Add(stopBuiltOnWorkerThread); // cross-thread\n// after\nstopBuiltOnWorkerThread.Freeze();\nuiElementBrush.GradientStops.Add(stopBuiltOnWorkerThread); // frozen: OK","handlingStrategy":"validation","validationCode":"if (owner.Dispatcher != null && child.Dispatcher != null && owner.Dispatcher != child.Dispatcher && !child.IsFrozen)\n    child = (Freezable)child.GetAsFrozen(); // or marshal to owner's thread","typeGuard":"static bool SameThreadOrFreezable(System.Windows.Freezable owner, System.Windows.Freezable child) => child.IsFrozen || owner.Dispatcher == child.Dispatcher || child.Dispatcher == null;","tryCatchPattern":"try { owner.SetValue(prop, child); }\ncatch (InvalidOperationException ex) { log.Error(\"Cross-thread Freezable child; freeze or marshal first\", ex); }","preventionTips":["Freeze any Freezable produced on background threads before handing it to the UI thread","Build UI-bound Freezables on the owner's Dispatcher","Compare Dispatchers before composing object graphs across threads"],"tags":["wpf","freezable","threading","dispatcher"],"backgroundTag":"cross-thread-object-access","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}