Repository navigation
Reject a non-positive spawn_rate in new_dispatch() - #3528
Merged
Merged
Conversation
A negative spawn rate made the wait between dispatch iterations negative, so ramping-up turned into an instant burst of the whole load, and a zero rate raised ZeroDivisionError. Both come from the same missing check that target_user_count already has.
This was referenced Oct 3, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
UsersDispatcher.new_dispatch()already rejects a negativetarget_user_count, but never looks atspawn_rate. This adds the missing sibling check:plus a regression test in
locust/test/test_dispatch.py.Fixes #3527
Why
new_dispatch()feeds the rate straight into the ramp-up math (locust/dispatch.py:206-208), and_wait_between_dispatch_iteration_context()clamps the result withmax(0.0, ...)(:291), so two silent/lethal things happen for callers that don't go through the CLI parser (web UI/swarm, programmatic use perdocs/use-as-lib.rst,LoadTestShape.tick()):0— the entire load is spawned in one instant burst instead of the requested ramp, while/swarmstill replies200 {"success": true, "message": "Swarming started"}. Measured with 5 users:spawn_rate=1→ 4.05 s to reach 5 users,spawn_rate=-1→ 0.00 s.spawn_rate=0raisesZeroDivisionError: division by zerofromdispatch.py:208, killing the shape/start greenlet with an error that never mentionsspawn_rate; the measured result is that the test never starts (user_countstays 0, state staysready).> 0is the contract the project already states for this value —--spawn-rateis parsed withgt_zero(float)inargument_parser.py:492, including in the released 2.46.6 — so the guard only moves that existing contract down to the entry points that bypass the parser. It raises before any dispatcher state is mutated, and no internal caller passes a non-positive rate (runners.py:505/:783only relay values that already passed the CLI guard, or the master's storedspawn_rate), so CLI-driven and distributed runs are unaffected.How it was checked
TestSpawnRateValidationcovers0,0.0,-0.5,-1,-1000: RED on555ad9e5(ValueError not raisedfor all 5 subtests) → GREEN with the change (2 passed, 5 subtests passed)./swarmendpoint and on aLoadTestShapereturning(5, 0), both before and after the change (before: instant burst /ZeroDivisionError; after:ValueError: spawn_rate must be > 0, got ...), as pasted in spawn_rate is never validated: a negative rate spawns the whole load instantly and 0 raises ZeroDivisionError #3527.locust/test/test_dispatch.pyfull suite passes;uv run ruff check locust/,ruff format --checkandmypy locust/dispatch.pyare clean.test_runners.py+test_web.pyrun has failures on my Windows machine, but they are pre-existing and identical with and without this change — I re-ran both suites against the untouched555ad9e5and got the same set (the web ones are500/PermissionErroron temp-file teardown in this environment, i.e. the/page and CSV/TLS artifacts; oneTestStopTimeoutcase is timing-flaky and passes when run alone).AI assistance disclosure: this change was produced by an AI coding assistant in a scripted session. The
reproductions, the RED/GREEN runs and the
ruff/mypy/pytestoutput quoted above come from thatsession, and the numbers were copied from the actual command output rather than estimated.