Cysharp/UniTask · error · ArgumentOutOfRangeException
Delay does not allow minus delayTimeSpan. delayTimeSpan:
Error message
Delay does not allow minus delayTimeSpan. delayTimeSpan:
What it means
Thrown by UniTask.Delay(TimeSpan) when delayTimeSpan is negative. UniTask delays accumulate elapsed time against the target duration; a negative duration cannot elapse. The guard catches sign errors early, before the promise is created.
Source
Thrown at src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.Delay.cs:169
}
public static UniTask Delay(TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
var delayType = ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime;
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
}
public static UniTask Delay(int millisecondsDelay, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
var delayTimeSpan = TimeSpan.FromMilliseconds(millisecondsDelay);
return Delay(delayTimeSpan, delayType, delayTiming, cancellationToken, cancelImmediately);
}
public static UniTask Delay(TimeSpan delayTimeSpan, DelayType delayType, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
{
if (delayTimeSpan < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException("Delay does not allow minus delayTimeSpan. delayTimeSpan:" + delayTimeSpan);
}
#if UNITY_EDITOR
// force use Realtime.
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
{
delayType = DelayType.Realtime;
}
#endif
switch (delayType)
{
case DelayType.UnscaledDeltaTime:
{
return new UniTask(DelayIgnoreTimeScalePromise.Create(delayTimeSpan, delayTiming, cancellationToken, cancelImmediately, out var token), token);
}
case DelayType.Realtime:
{View on GitHub (pinned to ceac8d6946)
Solutions
- Clamp the TimeSpan to zero: delayTimeSpan < TimeSpan.Zero ? TimeSpan.Zero : delayTimeSpan
- Check if the deadline has already passed and skip the delay entirely
- Use TimeSpan.Max(TimeSpan.Zero, computedSpan) (C# 8.0+ for TimeSpan supports CompareTo, otherwise use a ternary)
Example fix
// before
var wait = deadline - DateTime.UtcNow; // could be negative
await UniTask.Delay(wait);
// after
var wait = deadline - DateTime.UtcNow;
if (wait > TimeSpan.Zero)
await UniTask.Delay(wait); Defensive patterns
Strategy: validation
Validate before calling
TimeSpan safeDelay = delayTimeSpan < TimeSpan.Zero ? TimeSpan.Zero : delayTimeSpan; await UniTask.Delay(safeDelay);
Prevention
- Check if a deadline has already passed before computing the delay
- Clamp TimeSpan values to TimeSpan.Zero minimum
- Log computed delay values during development to catch sign errors
When it happens
Trigger: Calling UniTask.Delay(TimeSpan.FromMilliseconds(-100)), or passing a TimeSpan computed from a subtraction that yields a negative result (e.g., deadline - now when the deadline has already passed).
Common situations: Timeout calculations where the deadline has already passed: TimeSpan.FromSeconds(targetTime - Time.time). Deserialization or configuration that produces negative durations. Clock skew in network time calculations.
Related errors
- Delay does not allow minus delayFrameCount. delayFrameCount:
- The tasks argument contains no tasks.
- Can not trigger itself in iterating.
- handler
- exception
AI-assisted analysis of Cysharp/UniTask@ceac8d6946 (2026-08-13).
Data as JSON: /api/errors/cc492649953b980e.
Report an issue: GitHub.