dotnet/wpf · error · InvalidOperationException

Storyboard_PropertyPathMustPointToDependencyObject

Storyboard_PropertyPathMustPointToDependencyObject

Error message

SR.Format(SR.Storyboard_PropertyPathMustPointToDependencyObject, AccessorName(path, i-1), path.Path)

What it means

Every intermediate object in an animation property path must be a DependencyObject so WPF can attach clocks to its properties. VerifyPathIsAnimatable throws this InvalidOperationException when a path segment (i > 0) resolves to a plain CLR object instead.

Solutions

  1. Make every intermediate step a DependencyObject; animate only DependencyObject.(DependencyProperty) chains.
  2. Change intermediate properties on your classes to DependencyProperties on DependencyObject-derived types.
  3. Trim the path so it ends at the DependencyProperty instead of continuing into a CLR object.
  4. Use attached-property syntax to route through known DOs.
  5. Inspect the reported accessor and replace that segment with a DO-based path.

Example fix

// before
new PropertyPath("MyPoco.Name.Length"); // MyPoco is a plain CLR object
// after
new PropertyPath("(UIElement.Opacity)"); // DO + DP chain
Defensive patterns

Strategy: validation

Validate before calling

static bool PathIntermediatesAreDO(object root, PropertyPath path)
{
    try {
        path.Evaluate(root);
        for (int i = 0; i < path.Get getItemCount; i++)
            if (!(path.GetItem(i) is DependencyObject) && i > 0) return false;
        return true;
    } catch { return false; }
}

Type guard

bool IsDependencyObject(object o) => o is DependencyObject;

Try / catch

try { storyboard.Begin(target); }
catch (InvalidOperationException ex) when (ex.Message.Contains("DependencyObject"))
{
    // restructure the path through DO/DP objects only
}

Prevention

When it happens

Trigger: A Storyboard.TargetProperty path walks through a non-DependencyObject — e.g. an intermediate property returns a plain POCO, struct, or string, and the path tries to descend further into it.

Common situations: Pathing through non-DP CLR wrappers; animating through Point/Size structs treated as intermediates; path segments through collections of plain objects; mis-ordered path where the animatable property segment is followed by extra segments.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Media/Animation/Storyboard.cs:745

                    {
                        // i == 0 means the targeted object itself is a frozen Freezable.
                        //  This need a different error message.
                        throw new InvalidOperationException(SR.Format(SR.Storyboard_ImmutableTargetNotSupported, path.Path));
                    }
                }
            }

            // The last object + property pairing (the one we're actually going
            //  to stick the clock on) has further requirements.
            if( i == path.Length-1 )
            {
                DependencyObject intermediateDO = intermediateObject as DependencyObject;
                DependencyProperty intermediateDP = intermediateProperty as DependencyProperty;

                if( intermediateDO == null )
                {
                    Debug.Assert( i > 0, "The caller should not have set the PropertyPath context to a non DependencyObject." );
                    throw new InvalidOperationException(SR.Format(SR.Storyboard_PropertyPathMustPointToDependencyObject, AccessorName(path, i-1), path.Path));
                }

                if( intermediateDP == null )
                {
                    throw new InvalidOperationException(SR.Format(SR.Storyboard_PropertyPathMustPointToDependencyProperty, path.Path ));
                }

                if( checkingFrozenState && intermediateDO.IsSealed )
                {
                    throw new InvalidOperationException(SR.Format(SR.Storyboard_PropertyPathSealedCheckFailed, intermediateDP.Name, path.Path, intermediateDO));
                }

                if(!AnimationStorage.IsPropertyAnimatable(intermediateDO, intermediateDP) )
                {
                    throw new InvalidOperationException(SR.Format(SR.Storyboard_PropertyPathIncludesNonAnimatableProperty, path.Path, intermediateDP.Name));
                }
            }
        }

View on GitHub (pinned to 81131a70a4)