{"record":{"id":"02170dc6bbfe8631","repo":"stride3d/stride","slug":"thread-count-must-be-positive","errorCode":null,"errorMessage":"Thread count must be positive.","messagePattern":"Thread count must be positive\\.","errorType":"validation","errorClass":"ArgumentOutOfRangeException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs","lineNumber":1117,"sourceCode":"\n        private volatile bool _disposed;\n\n        /// <inheritdoc/>\n        public WorkerBufferPools WorkerPools { get; private set; }\n        /// <inheritdoc/>\n        public void* UnmanagedContext => _unmanagedContext;\n        /// <inheritdoc/>\n        public object? ManagedContext => _managedContext;\n\n        /// <summary>\n        /// Creates a new thread dispatcher with the given number of threads.\n        /// </summary>\n        /// <param name=\"threadCount\">Number of threads to dispatch on each invocation.</param>\n        /// <param name=\"threadPoolBlockAllocationSize\">Size of memory blocks to allocate for thread pools.</param>\n        public DispatcherWrapper(int threadCount, int threadPoolBlockAllocationSize = 16384)\n        {\n            if (threadCount <= 0)\n                throw new ArgumentOutOfRangeException(nameof(threadCount), \"Thread count must be positive.\");\n            _threadCount = threadCount;\n            WorkerPools = new WorkerBufferPools(threadCount, threadPoolBlockAllocationSize);\n        }\n\n        private void DispatchThread(int workerIndex)\n        {\n            switch (_workerType)\n            {\n                case WorkerType.Managed: _managedWorker!(workerIndex); break;\n                case WorkerType.Unmanaged: _unmanagedWorker(workerIndex, this); break;\n            }\n        }\n\n        private void SignalThreads(int maximumWorkerCount)\n        {\n            int workersToSignal = maximumWorkerCount < _threadCount ? maximumWorkerCount : _threadCount;\n            Dispatcher.ForBatched(workersToSignal, new Job(this));\n        }","sourceCodeStart":1099,"sourceCodeEnd":1135,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs#L1099-L1135","documentation":"DispatcherWrapper's constructor validates that the number of worker threads dispatched per invocation is strictly positive, since it sizes WorkerBufferPools and schedules work across threads. A zero or negative threadCount makes dispatch meaningless and would corrupt pool sizing, so it throws ArgumentOutOfRangeException.","triggerScenarios":"Constructing new DispatcherWrapper(0) or with a negative value, typically from a thread-count setting read from config/environment that defaulted to 0 or wasn't initialized.","commonSituations":"Environment.ProcessorCount returning unexpectedly, config parsing producing 0, or custom BepuSimulation threading setup passing an unset variable.","solutions":["Pass at least 1 thread: new DispatcherWrapper(Math.Max(1, threadCount));","Fix the thread-count source so it yields a positive value (e.g. Environment.ProcessorCount).","Use the default constructor path / library defaults instead of hand-rolling a dispatcher."],"exampleFix":"// before\nvar dispatcher = new DispatcherWrapper(config.ThreadCount); // 0\n// after\nvar dispatcher = new DispatcherWrapper(Math.Max(1, config.ThreadCount));","handlingStrategy":"validation","validationCode":"int threadCount = Math.Max(1, configuredThreadCount);\nvar dispatcher = new DispatcherWrapper(threadCount);","typeGuard":null,"tryCatchPattern":"try { d = new DispatcherWrapper(cfg.ThreadCount); } catch (ArgumentOutOfRangeException) { d = new DispatcherWrapper(Environment.ProcessorCount); }","preventionTips":["Clamp config-derived thread counts with Math.Max(1, value)","Default to Environment.ProcessorCount when config is missing","Validate numeric settings after deserialization"],"tags":["physics","threading","argument"],"backgroundTag":"value-out-of-range","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}