ppy/osu · error · NotSupportedException

Adjust via RepeatCount instead

Error message

Adjust via RepeatCount instead

What it means

Slider.Duration is a computed property derived from EndTime - StartTime, where EndTime depends on SpanCount, Path.Distance, and Velocity. The setter is intentionally disabled with NotSupportedException because setting duration directly would require decomposing it into multiple interdependent factors. The message directs callers to RepeatCount, which adjusts SpanCount and thus the effective duration.

Source

Thrown at osu.Game.Rulesets.Osu/Objects/Slider.cs:34

using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;

namespace osu.Game.Rulesets.Osu.Objects
{
    public class Slider : OsuHitObject, IHasPathWithRepeats, IHasSliderVelocity, IHasGenerateTicks
    {
        public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;

        [JsonIgnore]
        public double Duration
        {
            get => EndTime - StartTime;
            set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
        }

        public override IList<HitSampleInfo> AuxiliarySamples => CreateSlidingSamples().Concat(TailSamples).ToArray();

        private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();

        public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);

        public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);

        private readonly SliderPath path = new SliderPath { OptimiseCatmull = true };

        public SliderPath Path
        {
            get => path;
            set
            {
                path.ControlPoints.Clear();

View on GitHub (pinned to d9c73e12ad)

Solutions

  1. Set RepeatCount instead to adjust the number of spans: slider.RepeatCount = n.
  2. If adjusting velocity, set SliderVelocityMultiplier or the IHasSliderVelocity interface.
  3. If adjusting path length, modify Path control points to change Path.Distance.
  4. If using a serialization framework, add [JsonIgnore] or a custom contract resolver to skip the Duration setter.

Example fix

// before
slider.Duration = 2000; // throws NotSupportedException

// after — adjust via RepeatCount (each repeat adds one span of Path.Distance / Velocity)
slider.RepeatCount = 1; // adds one additional span
Defensive patterns

Strategy: validation

Validate before calling

// Do not assign to Duration — compute RepeatCount from desired duration instead
double desiredDuration = 2000;
double spanDuration = slider.Path.Distance / slider.Velocity;
int requiredSpans = Math.Max(1, (int)Math.Round(desiredDuration / spanDuration));
slider.RepeatCount = requiredSpans - 1;

Prevention

When it happens

Trigger: Any code that attempts to assign to Slider.Duration, e.g., slider.Duration = 1000. This includes serialization frameworks that try to round-trip the property, auto-mappers, or ported code from another ruleset where Duration is settable.

Common situations: Deserialization frameworks (e.g., JSON.NET) that attempt to set Duration if it appears in serialization; porting hit-object construction logic from a ruleset where Duration is writable; code-generated or reflected property setters.

Related errors


AI-assisted analysis of ppy/osu@d9c73e12ad (2026-08-13). Data as JSON: /api/errors/c9d97b40fc71301d. Report an issue: GitHub.