dotnet/wpf · error · InvalidOperationException

Animation_ReturnedUnsetValueInstance

Animation_ReturnedUnsetValueInstance

Error message

SR.Animation_ReturnedUnsetValueInstance (Animation_ReturnedUnsetValueInstance)

What it means

A custom AnimationClock's GetCurrentValue returned DependencyProperty.UnsetValue, which is reserved for the property system and never a legal animated value. WPF throws InvalidOperationException naming the timeline type, property, and owner type.

Solutions

  1. Return the incoming baseValue (or a valid default) instead of UnsetValue when the animation has no value to contribute
  2. Remove sentinel checks that leak UnsetValue out of GetCurrentValue
  3. Add unit tests for the custom clock covering the 'nothing to apply' path

Example fix

// before
public override object GetCurrentValue(object baseValue, object d) => DependencyProperty.UnsetValue; // throws
// after
public override object GetCurrentValue(object baseValue, object d) => baseValue;
Defensive patterns

Strategy: validation

Validate before calling

object result = clock.GetCurrentValue(baseValue, dest);
if (result == DependencyProperty.UnsetValue) result = baseValue;

Type guard

static bool IsValidReturnValue(object v) => v != DependencyProperty.UnsetValue;

Prevention

When it happens

Trigger: A custom AnimationTimeline/animation clock returning UnsetValue from GetCurrentValue; misuse of UnsetValue as a sentinel in animation layering code.

Common situations: Custom animations that try to signal 'no value' by returning UnsetValue instead of the base value.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/AnimationStorage.cs:985

                    currentLayerValue = storage._snapshotValue;
                }

                Debug.Assert(clocksCount > 0);
                Debug.Assert(defaultDestinationValue != DependencyProperty.UnsetValue);

                for (int i = 0; i < clocksCount; i++)
                {
                    if (clocks[i].CurrentState != ClockState.Stopped)
                    {
                        hasActiveOrFillingClock = true;

                        currentLayerValue = clocks[i].GetCurrentValue(currentLayerValue, defaultDestinationValue);

                        // An animation may not return DependencyProperty.UnsetValue as its
                        // current value.
                        if (currentLayerValue == DependencyProperty.UnsetValue)
                        {
                            throw new InvalidOperationException(SR.Format(
                                SR.Animation_ReturnedUnsetValueInstance,
                                clocks[i].Timeline.GetType().FullName,
                                dp.Name,
                                d.GetType().FullName));
                        }
                    }
                }

                // The currentLayerValue only applies when there is at least one
                // active or filling clock.
                if (hasActiveOrFillingClock)
                {
                    currentPropertyValue = currentLayerValue;
                }
            }

            // We have a calculated currentPropertyValue, so return it if the type matches.
            if (DependencyProperty.IsValidType(currentPropertyValue, dp.PropertyType))

View on GitHub (pinned to 81131a70a4)