{"record":{"id":"1055135b8cd9d322","repo":"HangfireIO/Hangfire","slug":"at-least-one-non-started-thread-should-be-created","errorCode":null,"errorMessage":"At least one non-started thread should be created.","messagePattern":"At least one non-started thread should be created\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.Core/Processing/BackgroundTaskScheduler.cs","lineNumber":125,"sourceCode":"            [CanBeNull] Action<Exception> exceptionHandler)\n        {\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            _exceptionHandler = exceptionHandler;\n            _semaphore = new Semaphore(0, Int32.MaxValue);\n\n            // Stopped event should always be the first in this array, see the DispatchLoop method.\n            _waitHandles = new WaitHandle[] { _stopped, _semaphore };\n\n#if !NETSTANDARD1_3\n            AppDomainUnloadMonitor.EnsureInitialized();\n#endif\n\n            _threads = threadFactory(DispatchLoop)?.ToArray();\n\n            if (_threads == null || _threads.Length == 0)\n            {\n                throw new ArgumentException(\"At least one non-started thread should be created.\", nameof(threadFactory));\n            }\n\n            if (_threads.Any(static thread => thread == null || (thread.ThreadState & ThreadState.Unstarted) == 0))\n            {\n                throw new ArgumentException(\"All the threads should be non-null and in the ThreadState.Unstarted state.\", nameof(threadFactory));\n            }\n\n            foreach (var thread in _threads)\n            {\n                thread.Start();\n            }\n\n            _ourThreadIds = new HashSet<int>(_threads.Select(static x => x.ManagedThreadId));\n        }\n\n        /// <inheritdoc />\n        public override int MaximumConcurrencyLevel => _threads.Length;\n","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.Core/Processing/BackgroundTaskScheduler.cs#L107-L143","documentation":"Thrown by the BackgroundTaskScheduler constructor when the threadFactory returns null or an empty collection. The scheduler needs at least one dedicated thread to run its dispatch loop and to report MaximumConcurrencyLevel, so zero threads is unusable.","triggerScenarios":"A threadFactory that returns null, returns an empty enumerable, or whose Where/filter removes all threads. Distinct from the null-thread case (error 173) — here the collection itself is empty.","commonSituations":"A factory that filters threads by a condition that matches none; a factory that returns null on a fast-path; a threadCount of zero feeding a factory that yields nothing.","solutions":["Ensure the factory returns at least one thread for any input.","If deriving count from config, clamp the count to >= 1 before building threads.","Default to Environment.ProcessorCount when the configured count is unavailable."],"exampleFix":"// before\nFunc<ThreadStart, IEnumerable<Thread>> factory = start =>\n    Enumerable.Range(0, threadCount).Where(i => false).Select(i => new Thread(start)); // empty\n\n// after\nvar count = Math.Max(1, threadCount);\nFunc<ThreadStart, IEnumerable<Thread>> factory = start =>\n    Enumerable.Range(0, count).Select(i => new Thread(start) { IsBackground = true });","handlingStrategy":"validation","validationCode":"Func<ThreadStart, IEnumerable<Thread>> factory = start =>\n    Enumerable.Range(0, Math.Max(1, threadCount))\n              .Select(i => new Thread(start) { IsBackground = true });\n// ensure the factory never returns an empty collection","typeGuard":null,"tryCatchPattern":"try { var s = new BackgroundTaskScheduler(factory, handler); }\ncatch (ArgumentException ex) when (ex.ParamName == nameof(threadFactory))\n{ /* rebuild factory with a guaranteed non-empty count */ }","preventionTips":["Clamp threadCount to >= 1 before generating threads.","Avoid filters that can remove all threads from the result.","Prefer Environment.ProcessorCount as a fallback count."],"tags":["hangfire","argument-validation","task-scheduler","threading"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}