HangfireIO/Hangfire · error · ArgumentException

JobStorage.JobExpirationTimeout value should be equal or…

Error message

JobStorage.JobExpirationTimeout value should be equal or greater than zero.

What it means

Thrown by the JobStorage.JobExpirationTimeout setter when the value is a negative TimeSpan. This timeout controls how long completed/failed jobs remain in storage before automatic cleanup; a negative value is logically invalid.

Solutions

  1. Use TimeSpan.Zero to disable expiration delay, or a positive value like TimeSpan.FromDays(1)
  2. Validate the configuration value is non-negative before assigning
  3. Use Math.Max(TimeSpan.Zero, parsedTimespan) as a safety guard

Example fix

// before
storage.JobExpirationTimeout = TimeSpan.FromSeconds(config.TimeoutSeconds);
// config.TimeoutSeconds is -30

// after
var seconds = Math.Max(0, config.TimeoutSeconds);
storage.JobExpirationTimeout = TimeSpan.FromSeconds(seconds);
Defensive patterns

Strategy: validation

Validate before calling

var timeout = TimeSpan.FromSeconds(config.TimeoutSeconds);
if (timeout < TimeSpan.Zero) timeout = TimeSpan.Zero;
storage.JobExpirationTimeout = timeout;

Type guard

value >= TimeSpan.Zero

Prevention

When it happens

Trigger: Setting JobStorage.Current.JobExpirationTimeout = TimeSpan.FromMinutes(-5) or passing a negative TimeSpan value. Also occurs when computing the timeout from a configuration value that was parsed as a negative number.

Common situations: A misconfigured app setting (e.g., JobExpirationTimeout=-1 in config), an arithmetic error computing the timeout, or a deserialized TimeSpan from a config file with a negative value.

Understand the failure class

Related errors


AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13). Data as JSON: /api/errors/644d3676ca11b0ae. Report an issue: GitHub.

Appendix: source

Thrown at src/Hangfire.Core/JobStorage.cs:74

            {
                lock (LockObject)
                {
                    _current = value;
                }
            }
        }

        public TimeSpan JobExpirationTimeout
        {
            get
            {
                return _jobExpirationTimeout;
            }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    throw new ArgumentException("JobStorage.JobExpirationTimeout value should be equal or greater than zero.", nameof(value));
                }

                _jobExpirationTimeout = value;
            }
        }

        public virtual bool LinearizableReads => false;

        public abstract IMonitoringApi GetMonitoringApi();

        public abstract IStorageConnection GetConnection();

        public virtual IStorageConnection GetReadOnlyConnection()
        {
            return GetConnection();
        }

#pragma warning disable 618

View on GitHub (pinned to c236dd0f93)