{"record":{"id":"b74f8c987bedeb88","repo":"HangfireIO/Hangfire","slug":"at-least-one-unstarted-thread-should-be-created","errorCode":null,"errorMessage":"At least one unstarted thread should be created.","messagePattern":"At least one unstarted thread should be created\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.Core/Processing/BackgroundDispatcher.cs","lineNumber":59,"sourceCode":"            [NotNull] Action<Guid, object> action,\n            [CanBeNull] object state,\n            [NotNull] Func<ThreadStart, IEnumerable<Thread>> threadFactory)\n        {\n            if (threadFactory == null) throw new ArgumentNullException(nameof(threadFactory));\n\n            _execution = execution ?? throw new ArgumentNullException(nameof(execution));\n            _action = action ?? throw new ArgumentNullException(nameof(action));\n            _state = state;\n\n#if !NETSTANDARD1_3\n            AppDomainUnloadMonitor.EnsureInitialized();\n#endif\n\n            var threads = threadFactory(DispatchLoop)?.ToArray();\n\n            if (threads == null || threads.Length == 0)\n            {\n                throw new ArgumentException(\"At least one unstarted 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            _stopped = new CountdownEvent(threads.Length);\n\n            foreach (var thread in threads)\n            {\n                thread.Start();\n            }\n        }\n\n        public bool Wait(TimeSpan timeout)\n        {\n            return _stopped.WaitHandle.WaitOne(timeout);","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.Core/Processing/BackgroundDispatcher.cs#L41-L77","documentation":"Thrown as ArgumentException (message 'At least one unstarted thread should be created.') by the BackgroundDispatcher constructor when the threadFactory returns null or an empty collection. BackgroundDispatcher needs at least one worker thread to run the dispatch loop; zero threads means no processing can occur, so the constructor rejects it at line 59.","triggerScenarios":"Constructing a BackgroundDispatcher where the supplied Func<ThreadStart, IEnumerable<Thread>> returns null or an empty sequence (e.g. a factory that filters out all threads, returns Array.Empty<Thread>(), or yields no elements). The check at lines 57-60 fires before any thread is started.","commonSituations":"A custom threadFactory that conditionally returns an empty collection (e.g. based on a worker count of zero, or a thread-creation throttle that returned nothing); a factory that returns null on a resource-allocation failure; misconfiguration where the worker/thread count is zero. Because BackgroundDispatcher is internal, this normally surfaces only via custom processing servers or tests.","solutions":["Ensure the threadFactory always returns at least one Thread (matching the configured worker/concurrency count, minimum 1).","Validate the configured worker count is >= 1 before constructing the dispatcher or its factory.","If the factory can fail to allocate a thread, throw a descriptive exception there rather than returning an empty collection."],"exampleFix":"// before\nFunc<ThreadStart, IEnumerable<Thread>> factory =\n    start => Enumerable.Empty<Thread>(); // throws\n\n// after\nFunc<ThreadStart, IEnumerable<Thread>> factory =\n    start => Enumerable.Range(0, Math.Max(1, workerCount))\n        .Select(_ => new Thread(start) { IsBackground = true });","handlingStrategy":"validation","validationCode":"if (workerCount < 1) throw new ArgumentOutOfRangeException(nameof(workerCount));\nFunc<ThreadStart, IEnumerable<Thread>> factory =\n    start => Enumerable.Range(0, workerCount)\n        .Select(_ => new Thread(start) { IsBackground = true });","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the configured worker/thread count is >= 1 before building the factory.","Make the factory throw on allocation failure rather than returning an empty collection.","Use the public BackgroundJobServer API which supplies a correct factory internally."],"tags":["argument","processing","threading","configuration","internal-api"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}