{"record":{"id":"784bfc6572620906","repo":"dotnet/wpf","slug":"sr-sourcesmustbeinsamethread","errorCode":null,"errorMessage":"SR.SourcesMustBeInSameThread","messagePattern":"SR\\.SourcesMustBeInSameThread","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs","lineNumber":2437,"sourceCode":"                        // Set the updated struct back into the source's _localStore.\n                        DependentListMapField.SetValue(source.DependencyObject, dependentListMap);\n                    }\n                }\n            }\n        }\n\n        internal static void ValidateSources(DependencyObject d, DependencySource[] newSources, Expression expr)\n        {\n            // Make sure all Sources are owned by the same thread.\n            if (newSources != null)\n            {\n                Dispatcher dispatcher = d.Dispatcher;\n                for (int i = 0; i < newSources.Length; i++)\n                {\n                    Dispatcher sourceDispatcher = newSources[i].DependencyObject.Dispatcher;\n                    if (sourceDispatcher != dispatcher && !(expr.SupportsUnboundSources && sourceDispatcher == null))\n                    {\n                        throw new ArgumentException(SR.SourcesMustBeInSameThread);\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// Register the two callbacks that are used to implement the \"alternative\n        /// Expression storage\" feature, and return the two methods used to access\n        /// the feature.\n        /// </summary>\n        /// <remarks>\n        /// This method should only be called (once) from the Framework.  It should\n        /// not be called directly by users.\n        /// </remarks>\n        internal static void RegisterForAlternativeExpressionStorage(\n                            AlternativeExpressionStorageCallback getExpressionCore,\n                            out AlternativeExpressionStorageCallback getExpression)\n        {","sourceCodeStart":2419,"sourceCodeEnd":2455,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs#L2419-L2455","documentation":"Thrown by UpdateExpressionSource (source-update path invoked from expression handling) when a new expression source's Dispatcher differs from the target DependencyObject's Dispatcher. Expression sources must live on the same thread as the dependant object, unless the expression declares SupportsUnboundSources and the source has no dispatcher.","triggerScenarios":"An Expression (custom Binding-like object) declares DependencySources whose DependencyObjects were created on (or marshaled to) a different thread than the target; calling ChangeSources with cross-thread sources.","commonSituations":"Background-thread-created objects used as binding sources, dispatcher affinity mistakes when moving elements between threads, custom markup extensions returning sources from another thread.","solutions":["Create all source DependencyObjects on the same thread as the target","If sources are genuinely thread-free, use an expression type whose SupportsUnboundSources is true and ensure source Dispatcher is null","Marshal source updates to the target's Dispatcher before calling source-change APIs","Restructure so the source is a plain (non-DependencyObject) value exposed through a property"],"exampleFix":"// before\nvar src = Task.Run(() => new DispatcherObject()).Result; // wrong thread\n// after\nvar src = new DispatcherObject(); // created on the UI thread\nexpr.ChangeSources(target, dp, new DependencySource[] { new DependencySource(src, prop) });","handlingStrategy":"validation","validationCode":"foreach (var s in newSources) {\n    if (s.DependencyObject.Dispatcher != target.Dispatcher && !(expr.SupportsUnboundSources && s.DependencyObject.Dispatcher == null))\n        throw new InvalidOperationException(\"Cross-thread expression source\");\n}\nexpr.ChangeSources(target, dp, newSources);","typeGuard":"static bool SameThread(DependencyObject a, DependencyObject b) => a.Dispatcher == b.Dispatcher;","tryCatchPattern":"try { expr.ChangeSources(target, dp, sources); }\ncatch (ArgumentException) when (sources.Any(s => s.DependencyObject.Dispatcher != target.Dispatcher)) { target.Dispatcher.Invoke(() => expr.ChangeSources(target, dp, sources)); }","preventionTips":["Create all source DependencyObjects on the target's thread","Marshal cross-thread updates with Dispatcher.Invoke/BeginInvoke","Only rely on SupportsUnboundSources for genuinely thread-free sources"],"tags":["wpf","threading","dispatcher"],"backgroundTag":"thread-interrupted","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-21T21:30:21.729Z"}