HangfireIO/Hangfire · error · ArgumentException
The `timeOut` value must be positive.
Error message
The `timeOut` value must be positive.
What it means
ArgumentException thrown by SqlServerConnection.RemoveTimedOutServers when the provided timeOut is not equal to its own Duration() — i.e. it is negative. The cleanup deletes Server rows whose LastHeartbeat is older than now - timeOut; a negative timeOut would invert the logic (deleting active servers), so Hangfire rejects it up front.
Source
Thrown at src/Hangfire.SqlServer/SqlServerConnection.cs:571
var affected = connection.Execute(
query,
new { id = serverId },
commandTimeout: storage.CommandTimeout);
if (affected == 0)
{
throw new BackgroundServerGoneException();
}
return affected;
}, serverId);
}
public override int RemoveTimedOutServers(TimeSpan timeOut)
{
if (timeOut.Duration() != timeOut)
{
throw new ArgumentException("The `timeOut` value must be positive.", nameof(timeOut));
}
return _storage.UseConnection(_dedicatedConnection, static (storage, connection, timeout) => connection.Execute(
storage.GetQueryFromTemplate(static schemaName =>
$@"delete s from [{schemaName}].Server s with (readpast, readcommitted) where LastHeartbeat < dateadd(ms, @timeoutMsNeg, sysutcdatetime())"),
new { timeoutMsNeg = timeout.Negate().TotalMilliseconds },
commandTimeout: storage.CommandTimeout), timeOut);
}
public override long GetSetCount(string key)
{
if (key == null) throw new ArgumentNullException(nameof(key));
return _storage.UseConnection(_dedicatedConnection, static (storage, connection, key) => connection.ExecuteScalar<long>(
storage.GetQueryFromTemplate(static schemaName =>
$@"select count(*) from [{schemaName}].[Set] with (forceseek) where [Key] = @key"),
new { key = key },
commandTimeout: storage.CommandTimeout), key);View on GitHub (pinned to c236dd0f93)
Solutions
- Pass a positive TimeSpan (e.g. TimeSpan.FromMinutes(5)) to RemoveTimedOutServers.
- Validate the configured timeout at startup: if (timeout < TimeSpan.Zero) throw a clear config error.
- Guard with TimeSpan.Max(TimeSpan.Zero, timeout) when deriving the value arithmetically.
Example fix
// before storage.RemoveTimedOutServers(now - recordedAt); // after storage.RemoveTimedOutServers(TimeSpan.FromMinutes(5));
Defensive patterns
Strategy: validation
Validate before calling
static TimeSpan AssertPositiveTimeout(TimeSpan t)
{
if (t.Duration() != t) throw new ArgumentException("timeOut must be positive.", nameof(t));
return t;
} Prevention
- Validate timeout config values at startup.
- Use named constants (e.g. ServerTimeout) instead of arithmetic to avoid sign mistakes.
- Unit-test RemoveTimedOutServers callers with negative inputs.
When it happens
Trigger: Calling RemoveTimedOutServers with a negative TimeSpan (or calling .Negate() unintentionally before passing it).
Common situations: Computing timeOut from a config value that defaulted to a negative number; subtracting instead of adding; passing -configuredTimeout by mistake; reading a malformed setting.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Timeout value should be equal to or greater than zero.
- The value must be a positive number
- Expression body should be of type `MethodCallExpression`
- The type `{method.DeclaringType}` must be derived from the `
- Argument count must be equal to method parameter count.
AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13).
Data as JSON: /api/errors/2b958cb72db201f5.
Report an issue: GitHub.