dotnet/wpf · error · InvalidOperationException

SR.Format(SR.CrossThreadAccessOfUnshareableFreezable…

Error message

SR.Format(SR.CrossThreadAccessOfUnshareableFreezable, freezable.GetType().FullName)

What it means

A Freezable used by the style/template (e.g. a Brush or Animation) is unfrozen and cannot be shared across threads. Because it cannot be frozen (CanFreeze == false) and is being accessed from a different thread than the one that created it, WPF throws InvalidOperationException to protect its dependency-object affinity.

Solutions

  1. Freeze the resource after creation: if (obj.CanFreeze) obj.Freeze();
  2. Ensure style resources are created and used on the same Dispatcher/thread
  3. Remove features that prevent freezing (animations, bindings) from shared resources
  4. Create a per-thread copy of the resource instead of sharing one instance

Example fix

// before
var b = new LinearGradientBrush();
b.BeginAnimation(LinearGradientBrush.ColorProperty, anim); // CanFreeze == false
style.Resources["bg"] = b;
// after
var b = new LinearGradientBrush(Colors.Red, Colors.Blue, 0);
b.Freeze(); // shareable across threads
style.Resources["bg"] = b;
Defensive patterns

Strategy: fallback

Validate before calling

static bool ShareableAcrossThreads(Freezable f)
{
    if (f.IsFrozen) return true;
    if (!f.CanFreeze) return false;
    return Dispatcher.CurrentDispatcher.Thread == f.Dispatcher?.Thread;
}

Type guard

bool IsThreadSafeResource(Freezable f) => f.IsFrozen || f.CanFreeze;

Try / catch

try { resource = FindResource(key) as Freezable; var clone = resource?.CloneCurrentValue(); clone?.Freeze(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("thread")) { resource = CreateFreshResource(); }

Prevention

When it happens

Trigger: Using an unfrozen, non-shareable Freezable (e.g. a Brush with animation or bound content) inside a style applied to elements on another thread; rendering templates on a background/render thread when a resource never got frozen.

Common situations: Producing visuals on worker threads; animations targeting resources that then cannot freeze; templates shared across Dispatcher-owned element trees in multi-window/thread scenarios.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/ab599a4477e11639. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/StyleHelper.cs:1596

                    object o = valueLookupList.List[i].Value;

                    if (o is MarkupExtension)
                    {
                        ProcessInstanceValue(target, childIndex, instanceValues, dp, i, apply);
                    }
                    else if ((freezable = o as Freezable) != null)
                    {
                        if (freezable.CheckAccess())
                        {
                            if (!freezable.IsFrozen)
                            {
                                ProcessInstanceValue(target, childIndex, instanceValues, dp, i, apply);
                            }
                        }
                        else
                        {
                            Debug.Assert(!freezable.CanFreeze, "If a freezable could have been frozen it would have been done by now.");
                            throw new InvalidOperationException(SR.Format(SR.CrossThreadAccessOfUnshareableFreezable, freezable.GetType().FullName));
                        }
                    }

                    break;
                }
            }
        }

        //
        //  This method
        //  1. Adds or removes per-instance state on the container/child (push model)
        //  2. Processes a single value that needs per-instance storage
        //
        internal static void ProcessInstanceValue(
            DependencyObject    target,
            int                 childIndex,
            HybridDictionary    instanceValues,
            DependencyProperty  dp,

View on GitHub (pinned to 81131a70a4)