dotnet/wpf · error · ArgumentException

SR.SourceChangeExpressionMismatch

Error message

SR.SourceChangeExpressionMismatch

What it means

Thrown by DependencyObject.ChangeExpressionSources when the local value stored on the target DependencyObject is no longer the Expression instance whose sources are being re-evaluated. The property system detected a mismatch between the expression it was asked to re-source and the actual local value, indicating the expression was replaced or cleared concurrently or out of order.

Solutions

  1. Verify the expression is still the current local value (ReadLocalValue) before changing its sources
  2. Re-set the expression with SetValue instead of calling ChangeExpressionSources on a stale instance
  3. Guard against re-entrant property changes while updating expression sources
  4. Use standard Binding/BindingExpression APIs rather than raw Expression plumbing

Example fix

// before
expr.ChangeSources(d, dp, newSources); // stale expr
// after
if (ReferenceEquals(d.ReadLocalValue(dp), expr)) { expr.ChangeSources(d, dp, newSources); }
Defensive patterns

Strategy: validation

Validate before calling

if (!ReferenceEquals(d.ReadLocalValue(dp), expr)) throw new InvalidOperationException("Expression no longer current");
expr.ChangeSources(d, dp, sources);

Type guard

static bool IsCurrentExpression(DependencyObject d, DependencyProperty dp, Expression expr) => ReferenceEquals(d.ReadLocalValue(dp), expr);

Try / catch

try { expr.ChangeSources(d, dp, sources); }
catch (ArgumentException) { d.SetValue(dp, expr); /* re-install then retry */ }

Prevention

When it happens

Trigger: Calling SetValue with an Expression, then modifying its sources via ChangeExpressionSources when the local value slot no longer reference-equals that expression; re-entrancy or double-set of expressions on the same property.

Common situations: Custom binding/expression implementations that call ChangeExpressionSources after the expression was overwritten, race conditions between expression replacement and source updates, misuse of internal expression APIs.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyObject.cs:1039

            object value = entry.IsCoercedWithCurrentValue ? entry.ModifiedValue.CoercedValue : entry.LocalValue;
            return !object.ReferenceEquals(value, DependencyProperty.UnsetValue);
        }

        //
        // Changes the sources of an existing Expression
        //
        internal static void ChangeExpressionSources(Expression expr, DependencyObject d, DependencyProperty dp, DependencySource[] newSources)
        {
            if (!expr.ForwardsInvalidations)
            {
                // Get current local value (should be provided Expression)
                // (No need to go through read local callback, just checking
                // for presence of Expression)
                EntryIndex entryIndex = d.LookupEntry(dp.GlobalIndex);

                if (!entryIndex.Found || (d._effectiveValues[entryIndex.Index].LocalValue != expr))
                {
                    throw new ArgumentException(SR.SourceChangeExpressionMismatch);
                }
            }

            // Get current sources
            // CALLBACK
            DependencySource[] currentSources = expr.GetSources();

            // Remove old
            if (currentSources != null)
            {
                UpdateSourceDependentLists(d, dp, currentSources, expr, false);  // Remove
            }

            // Add new
            if (newSources != null)
            {
                UpdateSourceDependentLists(d, dp, newSources, expr, true);  // Add
            }

View on GitHub (pinned to 81131a70a4)