stride3d/stride · error · ArgumentOutOfRangeException

Tick frequency must be greater than zero

Error message

Tick frequency must be greater than zero

What it means

ProfilingState.TickFrequency converts raw timestamps to TimeSpan and its setter enforces a strictly positive value; assigning 0 or a negative long throws ArgumentOutOfRangeException. GPU tick frequencies are set by rendering code, so a failed/absent GPU query can leave an invalid value that gets pushed here.

Solutions

  1. Only assign Stopwatch.Frequency or a queried positive GPU frequency
  2. Check for 0 before assignment and fall back to Stopwatch.Frequency
  3. Clamp: state.TickFrequency = Math.Max(1, queriedFrequency)

Example fix

// before
state.TickFrequency = gpuFrequency; // may be 0
// after
state.TickFrequency = gpuFrequency > 0 ? gpuFrequency : Stopwatch.Frequency;
Defensive patterns

Strategy: validation

Validate before calling

if (frequency <= 0) frequency = Stopwatch.Frequency;
state.TickFrequency = frequency;

Type guard

bool isValidTickFrequency(long v) => v > 0;

Try / catch

try { state.TickFrequency = frequency; }
catch (ArgumentOutOfRangeException) { state.TickFrequency = Stopwatch.Frequency; }

Prevention

When it happens

Trigger: profileState.TickFrequency = 0 (or negative) in custom profiling code, or GPU backend code that assigns an unqueried/failed frequency (e.g. 0 from an uninitialized timestamp-frequency query).

Common situations: Custom GPU profiling integrations where the driver returns 0 frequency; copying CPU profiling code and forgetting to initialize frequency; int/float division producing 0.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/854117a7384a2107. Report an issue: GitHub.

Appendix: source

Thrown at sources/core/Stride.Core/Diagnostics/ProfilingState.cs:67

    /// <summary>
    /// Gets the profiling key.
    /// </summary>
    /// <value>The profiling key.</value>
    public ProfilingKey ProfilingKey { get; }

    /// <summary>
    /// A list of attributes (dimensions) associated with this profiling state.
    /// </summary>
    public TagList Attributes { get; }

    /// <summary>
    /// Gets or sets the TickFrequency used to convert <see cref="long"/> timestamp to <see cref="TimeSpan"/>.
    /// By default for CPU events it's <see cref="Stopwatch.Frequency"/> and for GPU events it's set by the rendering code."/>
    /// </summary>
    public long TickFrequency
    {
        get => tickFrequency;
        set => tickFrequency = value > 0 ? value : throw new ArgumentOutOfRangeException(nameof(TickFrequency), "Tick frequency must be greater than zero");
    }

    /// <summary>
    /// Checks if the profiling key is enabled and update this instance. See remarks.
    /// </summary>
    /// <remarks>
    /// This can be used for long running profiling that are using markers and want to log markers if 
    /// the profiling was activated at runtime.
    /// </remarks>
    public void CheckIfEnabled()
    {
        isEnabled = Profiler.IsEnabled(ProfilingKey);
    }

    public void Dispose()
    {
        // Perform a Start event only if the profiling is running
        if (!isEnabled) return;

View on GitHub (pinned to 96fad776d2)