dotnet/yarp · error · InvalidOperationException
An uninitialized, or 'default', ValueStopwatch cannot be use
Error message
An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.
What it means
ValueStopwatch is a struct that stores only a start timestamp; reading Elapsed on a default(ValueStopwatch) (start timestamp 0) is meaningless, so the getter throws InvalidOperationException. The zero check is the library's way of detecting an uninitialized instance, since StartNew is the only constructor and a real timestamp can never be 0. YARP uses this internally for cheap timing in hot paths.
Source
Thrown at src/ReverseProxy/Utilities/ValueStopwatch.cs:37
private readonly long _startTimestamp;
private ValueStopwatch(long startTimestamp)
{
_startTimestamp = startTimestamp;
}
/// <summary>
/// Gets the time elapsed since the stopwatch was created with <see cref="StartNew"/>.
/// </summary>
public TimeSpan Elapsed
{
get
{
// Start timestamp can't be zero in an initialized ValueStopwatch. It would have to be literally the first thing executed when the machine boots to be 0.
// So it being 0 is a clear indication of default(ValueStopwatch)
if (_startTimestamp == 0)
{
throw new InvalidOperationException("An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.");
}
var end = Stopwatch.GetTimestamp();
var timestampDelta = end - _startTimestamp;
var ticks = (long)(_timestampToTicks * timestampDelta);
return new TimeSpan(ticks);
}
}
/// <summary>
/// Creates a new <see cref="ValueStopwatch"/> that is ready to be used.
/// </summary>
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());
}
View on GitHub (pinned to bd11867bee)
Solutions
- Initialize the stopwatch eagerly with ValueStopwatch.StartNew() at declaration or in the constructor.
- Track a separate bool _started flag and guard the Elapsed read behind it.
- Use ValueStopwatch? (nullable) and check HasValue before reading Elapsed.
- If timing is optional, prefer nullable ValueStopwatch? over a sentinel default instance.
Example fix
// before private ValueStopwatch _stopwatch; public TimeSpan Elapsed => _stopwatch.Elapsed; // throws if never started // after private ValueStopwatch? _stopwatch; public TimeSpan Elapsed => _stopwatch?.Elapsed ?? TimeSpan.Zero;
Defensive patterns
Strategy: validation
Validate before calling
ValueStopwatch? sw = null; // only start when needed: sw = ValueStopwatch.StartNew(); // read defensively: var elapsed = sw?.Elapsed ?? TimeSpan.Zero;
Type guard
static bool IsRunning(ValueStopwatch sw) => sw != default;
// usage:
if (IsRunning(_stopwatch)) { var e = _stopwatch.Elapsed; } Try / catch
try { var e = _stopwatch.Elapsed; }
catch (InvalidOperationException) { /* stopwatch not started; skip timing */ } Prevention
- Prefer ValueStopwatch? (nullable) over a bare struct field when timing is optional.
- Initialize stopwatches with StartNew() at the point of declaration, not in a later branch.
- Keep a bool _started flag alongside any conditional stopwatch.
- Avoid embedding ValueStopwatch in structs that may be default-constructed.
When it happens
Trigger: Declaring a ValueStopwatch field, property, or local without assigning ValueStopwatch.StartNew(), then reading .Elapsed. For example a struct holding an optional timer where the timer is left default in some code path, or a field initialized only inside an if-branch that is later read unconditionally.
Common situations: A conditional StartNew() inside an if-block but an unconditional Elapsed read; refactoring a class stopwatch into a struct field and forgetting the initializer; default-constructing a parent struct that embeds a ValueStopwatch; sharing a stopwatch across async paths where one path never starts it.
Related errors
- Header values must not be specified when using '{mode}'.
- Header values must be not be empty.
- Query parameter values must have at least one value.
- Query parameter values must not be specified when using '{na
- Query parameter values must not be empty.
AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13).
Data as JSON: /api/errors/be9932c4ca06e45f.
Report an issue: GitHub.