HangfireIO/Hangfire · error · ArgumentNullException

action

Error message

action

What it means

Thrown by SlowLogProfiler.InvokeMeasured when the action delegate (the work to measure) is null. SlowLogProfiler measures execution time and emits a slow-log warning when a threshold is exceeded. Passing a null action is a programming contract violation — there is nothing to measure and invoke.

Source

Thrown at src/Hangfire.Core/Profiling/SlowLogProfiler.cs:45

        private readonly ILog _logger;

        public SlowLogProfiler(ILog logger)
            : this(logger, DefaultThreshold)
        {
        }

        public SlowLogProfiler(ILog logger, TimeSpan threshold)
        {
            _threshold = threshold;
            _logger = logger;
        }

        public TResult InvokeMeasured<TInstance, TResult>(
            TInstance instance,
            Func<TInstance, TResult> action,
            Func<TInstance, string> messageFunc = null)
        {
            if (action == null) throw new ArgumentNullException(nameof(action));

            var startedAt = Environment.TickCount;

            // TODO: Change implementation to thread-based, once it is possible to query custom services everywhere
            using (new Timer(LogWarningMessage, null, _threshold, _threshold))
            {
                return action(instance);
            }

            void LogWarningMessage(object state)
            {
                var elapsedSec = unchecked(Environment.TickCount - startedAt) / 1_000;

                var type = instance?.ToString() ?? typeof(TInstance).ToString();
                var message = messageFunc?.Invoke(instance) ?? "(null)";

                _logger.Warn($"Slow log: {type} is still performing \"{message}\" after {elapsedSec} sec");
            }

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Pass a non-null Func<TInstance, TResult> delegate to InvokeMeasured.
  2. Guard the caller: if the action is null, skip the profiled call entirely instead of forwarding null.
  3. Verify the factory/expression producing the action returns a real delegate.

Example fix

// before
profiler.InvokeMeasured(instance, action: nullFunc);

// after
Func<MyType, MyResult> action = inst => DoWork(inst);
profiler.InvokeMeasured(instance, action);
Defensive patterns

Strategy: validation

Validate before calling

Func<TInstance, TResult> action = BuildAction();
if (action == null) throw new InvalidOperationException("No action to measure.");
profiler.InvokeMeasured(instance, action);

Prevention

When it happens

Trigger: Calling slowLogProfiler.InvokeMeasured(instance, null) where the Func<TInstance, TResult> action argument is null. Occurs when a caller builds the action lazily and a factory returns null, or when a code path passes an unresolved delegate.

Common situations: Custom Hangfire extensions that wrap operations with the profiler but conditionally pass null when no operation is needed. Refactoring that drops the delegate assignment. DI or factory method returning null for the action.

Related errors


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