dotnet/wpf · error · InvalidOperationException
SR.Format(SR.Storyboard_ComplexPathNotSupported…
Error message
SR.Format(SR.Storyboard_ComplexPathNotSupported, targetObject.GetType().ToString())
What it means
Thrown by VerifyComplexPathSupport when a storyboard uses a complex property path on a target object type that does not support it. Only specific types (e.g. those hooked into Storyboard.GetComplexPathValue, like certain Freezable/attached-property scenarios) accept complex paths; anything else is rejected rather than silently ignored.
Solutions
- Replace the complex path with a direct property target: set Storyboard.TargetName/TargetProperty to the element and the DependencyProperty directly.
- Break the animation into multiple storyboards, each targeting a single hop in the path on a supported type.
- If intermediate values must be animated, animate the intermediate object directly (give it a name or reference it as the animation target).
Example fix
// before
Storyboard.SetTargetProperty(sb, new PropertyPath("(0).(1)", new object[] { ... })); // target type doesn't support complex paths
// after
Storyboard.SetTargetName(sb, "myRect");
Storyboard.SetTargetProperty(sb, new PropertyPath("Opacity")); Defensive patterns
Strategy: validation
Validate before calling
// Only use complex paths on supported targets
if (path.Path.Contains("(") && !(targetObject is Freezable))
throw new NotSupportedException("Complex path not supported on " + targetObject.GetType()); Type guard
bool SupportsComplexPath(object target) => target is Freezable || target is FrameworkElement && !target.GetType().IsSealed && false; // prefer: keep paths simple for non-Freezables
Try / catch
try { storyboard.Begin(target, true); }
catch (InvalidOperationException ex) when (ex.Message.Contains("complex path")) { storyboard.Begin(simpleTarget, true); } Prevention
- Use single-hop TargetProperty strings on regular FrameworkElements
- Reserve complex paths for documented scenarios (e.g. Freezable sub-object animation)
- Split deep paths into sequential storyboards per hop
When it happens
Trigger: Storyboard.TargetProperty set to a complex path string while the resolved targetObject's type is not one of the types that call into Storyboard.GetComplexPathValue; ProcessComplexPath then invokes VerifyComplexPathSupport which throws.
Common situations: Copying a multi-segment property path (like '(0).(1)'-style attached/complex paths) onto an arbitrary custom control; animating a POCO wrapper object; WPF-to-Silverlight-style path syntax that is unsupported on the target type.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- SR.Format(SR.Storyboard_PropertyPathUnresolved, path.Path)
- CannotChangeAfterSealed
- CannotChangeAfterSealed
- SR.Format(SR.Storyboard_AnimationMismatch…
- SR.Storyboard_NeverApplied
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/bbe44f91153ba6be.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Media/Animation/Storyboard.cs:945
/// </summary>
private void VerifyComplexPathSupport( DependencyObject targetObject )
{
if(targetObject is FrameworkElement)
{
// FrameworkElement and derived types are supported.
return;
}
if(targetObject is FrameworkContentElement)
{
// FrameworkContentElement and derived types are supported.
return;
}
// ... and anything else that knows to call into Storyboard.GetComplexPathValue.
// Otherwise - throw.
throw new InvalidOperationException(SR.Format(SR.Storyboard_ComplexPathNotSupported, targetObject.GetType().ToString()));
}
/// <summary>
/// Check to see if there is a complex path that started with the
/// given target object and property. If so, process the complex path
/// information and return the results.
/// </summary>
internal static void GetComplexPathValue(
DependencyObject targetObject,
DependencyProperty targetProperty,
ref EffectiveValueEntry entry,
PropertyMetadata metadata)
{
CloneCacheEntry cacheEntry = GetComplexPathClone(targetObject, targetProperty);
if (cacheEntry != null)
{
object baseValue = entry.Value;View on GitHub (pinned to 81131a70a4)