{"record":{"id":"761f57eb3c9952cf","repo":"microsoft/FASTER","slug":"delay","errorCode":null,"errorMessage":"delay","messagePattern":"delay","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"cs/src/devices/AzureStorageDevice/LeaseTimer.cs","lineNumber":127,"sourceCode":"\n            Entry current;\n            while (true)\n            {\n                current = this.schedule[position];\n                if (current == null || Interlocked.CompareExchange<Entry>(ref this.schedule[position], null, current) == current)\n                {\n                    break;\n                }\n            }\n\n            current?.RunAll();\n        }\n\n        public Task Schedule(int delay, Func<Task> callback, CancellationToken token)\n        {\n            if ((delay / 1000) >= MaxDelay || delay < 0)\n            {\n                throw new ArgumentException(nameof(delay));\n            }\n\n            var entry = new Entry()\n            {\n               Tcs = new TaskCompletionSource<bool>(),\n               Callback = callback,\n            };\n\n            entry.Registration = token.Register(entry.Cancel);\n\n            while (true)\n            {\n                int targetPosition = (this.position + (delay * TicksPerSecond) / 1000) % (MaxDelay * TicksPerSecond);\n\n                if (targetPosition == this.position)\n                {\n                    return callback();\n                }","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/devices/AzureStorageDevice/LeaseTimer.cs#L109-L145","documentation":"LeaseTimer.Schedule validates its delay parameter: it must be non-negative and strictly less than MaxDelay seconds (delay/1000 < MaxDelay). An ArgumentException is thrown when the delay is negative or >= MaxDelay*1000 ms, because such a timer entry cannot be represented/scheduled safely.","triggerScenarios":"Calling LeaseTimer.Schedule with delay < 0, or with delay >= MaxDelay*1000 milliseconds (e.g. accidentally passing seconds instead of milliseconds, or computing a lease-renewal delay from a misconfigured value).","commonSituations":"Unit confusion (seconds vs milliseconds) when computing renewal delays; a config value like lease poll interval parsed as a huge number; arithmetic producing a negative delay from clock skew.","solutions":["Clamp the delay into [0, MaxDelay*1000) before calling Schedule.","Check unit conversion: Schedule expects milliseconds; convert seconds values with *1000 and keep under MaxDelay seconds.","Validate any config-sourced interval before passing it in."],"exampleFix":"// before\ntimer.Schedule(leaseDurationSeconds, callback, token); // wrong unit\n// after\nint delayMs = Math.Clamp((int)TimeSpan.FromSeconds(leaseDurationSeconds).TotalMilliseconds, 0, (LeaseTimer.MaxDelay * 1000) - 1);\ntimer.Schedule(delayMs, callback, token);","handlingStrategy":"validation","validationCode":"if (delay < 0 || delay >= LeaseTimer.MaxDelay * 1000)\n    throw new ArgumentOutOfRangeException(nameof(delay), $\"delay must be in [0, {LeaseTimer.MaxDelay * 1000}) ms\");","typeGuard":null,"tryCatchPattern":"try\n{\n    await timer.Schedule(delayMs, callback, token);\n}\ncatch (ArgumentException ex) when (ex.ParamName == \"delay\")\n{\n    // fix the computed delay and reschedule\n}","preventionTips":["Always pass delays in milliseconds; double-check seconds-to-ms conversions.","Clamp computed delays to [0, MaxDelay*1000) before scheduling.","Validate config-sourced interval values at load time.","Guard against negative results from clock-skew-based arithmetic."],"tags":["timer","argument-validation","azure-storage"],"backgroundTag":"argument-out-of-range","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}