{"record":{"id":"be9932c4ca06e45f","repo":"dotnet/yarp","slug":"an-uninitialized-or-default-valuestopwatch-can","errorCode":null,"errorMessage":"An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.","messagePattern":"An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/ReverseProxy/Utilities/ValueStopwatch.cs","lineNumber":37,"sourceCode":"    private readonly long _startTimestamp;\n\n    private ValueStopwatch(long startTimestamp)\n    {\n        _startTimestamp = startTimestamp;\n    }\n\n    /// <summary>\n    /// Gets the time elapsed since the stopwatch was created with <see cref=\"StartNew\"/>.\n    /// </summary>\n    public TimeSpan Elapsed\n    {\n        get\n        {\n            // 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.\n            // So it being 0 is a clear indication of default(ValueStopwatch)\n            if (_startTimestamp == 0)\n            {\n                throw new InvalidOperationException(\"An uninitialized, or 'default', ValueStopwatch cannot be used to get elapsed time.\");\n            }\n\n            var end = Stopwatch.GetTimestamp();\n            var timestampDelta = end - _startTimestamp;\n            var ticks = (long)(_timestampToTicks * timestampDelta);\n            return new TimeSpan(ticks);\n        }\n    }\n\n    /// <summary>\n    /// Creates a new <see cref=\"ValueStopwatch\"/> that is ready to be used.\n    /// </summary>\n    public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp());\n}\n","sourceCodeStart":19,"sourceCodeEnd":52,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/src/ReverseProxy/Utilities/ValueStopwatch.cs#L19-L52","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nprivate ValueStopwatch _stopwatch;\npublic TimeSpan Elapsed => _stopwatch.Elapsed; // throws if never started\n\n// after\nprivate ValueStopwatch? _stopwatch;\npublic TimeSpan Elapsed => _stopwatch?.Elapsed ?? TimeSpan.Zero;","handlingStrategy":"validation","validationCode":"ValueStopwatch? sw = null;\n// only start when needed:\nsw = ValueStopwatch.StartNew();\n// read defensively:\nvar elapsed = sw?.Elapsed ?? TimeSpan.Zero;","typeGuard":"static bool IsRunning(ValueStopwatch sw) => sw != default;\n// usage:\nif (IsRunning(_stopwatch)) { var e = _stopwatch.Elapsed; }","tryCatchPattern":"try { var e = _stopwatch.Elapsed; }\ncatch (InvalidOperationException) { /* stopwatch not started; skip timing */ }","preventionTips":["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."],"tags":["stopwatch","uninitialized","struct","timing","yarp"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}