OrchardCMS/OrchardCore · error · InvalidOperationException
An uninitialized, or 'default', ValueStopwatch cannot be…
Error message
An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.
What it means
ValueStopwatch is a struct wrapper around Stopwatch.GetTimestamp. A default(ValueStopwatch) has _startTimestamp == 0, meaning it was never started; GetElapsedTime refuses to compute a meaningless elapsed time and throws InvalidOperationException when IsActive is false.
Solutions
- Initialize with ValueStopwatch.StartNew() before any GetElapsedTime call.
- Check the IsActive property before calling GetElapsedTime and handle the inactive case.
- Make the field nullable or wrap it in a class so an uninitialized state is explicit.
- Audit failure/early-return paths that skip StartNew but still flow into timing code.
Example fix
// before
private ValueStopwatch _stopwatch;
public void Done() => Log(_stopwatch.GetElapsedTime()); // throws if never started
// after
private ValueStopwatch? _stopwatch;
public void Begin() => _stopwatch = ValueStopwatch.StartNew();
public void Done() { if (_stopwatch is { IsActive: true }) Log(_stopwatch.Value.GetElapsedTime()); } Defensive patterns
Strategy: type-guard
Validate before calling
if (!stopwatch.IsActive)
throw new InvalidOperationException("Stopwatch was never started."); Type guard
bool CanReadElapsed(in ValueStopwatch stopwatch) => stopwatch.IsActive;
Try / catch
try { var elapsed = stopwatch.GetElapsedTime(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("uninitialized"))
{
_logger.LogWarning("Attempted to read elapsed time from a default ValueStopwatch");
} Prevention
- Always initialize with ValueStopwatch.StartNew(), never default.
- Check IsActive before GetElapsedTime on fields that may be uninitialized.
- Wrap ValueStopwatch in a nullable field or factory-allocated holder so zero-value state is impossible to miss.
- Audit early-return code paths that skip StartNew.
When it happens
Trigger: Calling GetElapsedTime() on a ValueStopwatch that was never initialized via ValueStopwatch.StartNew() — e.g., an uninitialized field, a struct copied before StartNew, or a default-constructed struct passed into a method.
Common situations: Field declared but StartNew never called on some code path; Stopwatch struct stored in a dictionary and retrieved before insertion; default struct returned from a Try-pattern failure branch.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/b7bfaa0775e8f7cc.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore/Modules/Overrides/HttpClient/ValueStopwatch.cs:27
{
private readonly long _startTimestamp;
public readonly bool IsActive => _startTimestamp != 0;
private ValueStopwatch(long startTimestamp)
{
_startTimestamp = startTimestamp;
}
public static ValueStopwatch StartNew() => new(Stopwatch.GetTimestamp());
public TimeSpan GetElapsedTime()
{
// 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 (!IsActive)
{
throw new InvalidOperationException("An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.");
}
var end = Stopwatch.GetTimestamp();
return Stopwatch.GetElapsedTime(_startTimestamp, end);
}
}
View on GitHub (pinned to 4306c0717f)