You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
seed is ignored by the sampler, and a negative seed crashes the server #219
Found this while reading how the handlers set up sampling, then confirmed it on an M1 Max 64 GB (main @ 1b6befd, mlx-swift-lm @ 4f1fdaa, macOS 27, Xcode 27 / Swift 6.4, mlx-community/Qwen3-0.6B-4bit).
1. seed has no effect on sampling
Both handlers handle a request seed by seeding MLX's global generator:
but the samplers don't draw from the global generator. They are seeded through GenerateParameters.seed, and when that is nil each TopPSampler / CategoricalSampler creates its own entropy-seeded RandomState:
Neither GenerateParameters(...) call in Server.swift passes seed:, so the field is accepted and silently ignored.
Repro: the same request three times with temperature: 0.9, seed: 42:
"There's a very important thing to remember: if"
"The animal is **a bird**."
"The animal is a bird."
temperature: 0 is reproducible as expected, so it's the sampling path specifically, not prefill.
2. A negative seed crashes the server
UInt64(seed) traps on a negative Int:
curl -s localhost:5413/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"hi"}],"max_tokens":3,"seed":-1}'# curl: (52) Empty reply from server — and the process is gone
From the crash report:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
0 SwiftLM Swift runtime failure: Negative value is not representable
1 SwiftLM handleChatCompletion(request:bodyData:...) + 3660 (Server.swift:2387)
handleTextCompletion has the same conversion at L3561. OpenAI's seed is a plain integer, nothing stops a client from sending a negative one, and one such request takes down every other client's session with it.
Suggested fix
Pass the seed into GenerateParameters(seed:) so it reaches the sampler. The global MLXRandom.seed call can stay for the paths that call MLXRandom.categorical directly.
Convert with UInt64(bitPattern: Int64(seed)) instead of UInt64(seed).
Happy to send a PR with a contract test for both halves (same seed twice → identical output; negative seed → 200 and the server still answers /health).
Thanks for the detailed report. I checked it against main and the code matches your description:
Server.swift only calls MLXRandom.seed(...) (L2386-2388 and L3560-3562), while neither GenerateParameters(...) call (L2371, L3547) passes seed:. GenerateParameters.seed is what seeds the TopPSampler / CategoricalSampler RNG (mlx-swift-lm Evaluate.swift), so the request field is accepted but has no effect on those samplers.
UInt64(seed) traps for a negative Int, so a single request can take the whole server down.
I have not run your curl repro myself, so the reproducibility and crash behaviour is confirmed from the code only.
Your suggested fix looks right to us: pass the seed through GenerateParameters(seed:), keep the global MLXRandom.seed for paths that call MLXRandom.categorical directly, and convert with UInt64(bitPattern: Int64(seed)). A PR with a contract test for both halves (same seed twice gives identical output; negative seed gives 200 and /health still answers) would be very welcome.
Thanks for checking it against the code. The curl repros were real runs on an M1 Max, and the crash report in the issue is from one of them, so you don't need to re-run it on your side.
The PR is already up: #220. It does what you describe: GenerateParameters.seed set in both handlers, the global MLXRandom.seed kept for the paths that call MLXRandom.categorical directly, and UInt64(bitPattern: Int64(seed)) for the conversion. The contract script got both assertions (same seed twice → identical message; seed: -1 → 200 and /health still answers), plus a unit test for the mapping. CI is green across the matrix.
Activity
solderzzc commentedon Oct 9, 2026
Thanks for the detailed report. I checked it against main and the code matches your description:
Server.swiftonly callsMLXRandom.seed(...)(L2386-2388 and L3560-3562), while neitherGenerateParameters(...)call (L2371, L3547) passesseed:.GenerateParameters.seedis what seeds theTopPSampler/CategoricalSamplerRNG (mlx-swift-lmEvaluate.swift), so the request field is accepted but has no effect on those samplers.UInt64(seed)traps for a negativeInt, so a single request can take the whole server down.I have not run your curl repro myself, so the reproducibility and crash behaviour is confirmed from the code only.
Your suggested fix looks right to us: pass the seed through
GenerateParameters(seed:), keep the globalMLXRandom.seedfor paths that callMLXRandom.categoricaldirectly, and convert withUInt64(bitPattern: Int64(seed)). A PR with a contract test for both halves (same seed twice gives identical output; negative seed gives 200 and/healthstill answers) would be very welcome.HamedShams commentedon Oct 9, 2026
Thanks for checking it against the code. The curl repros were real runs on an M1 Max, and the crash report in the issue is from one of them, so you don't need to re-run it on your side.
The PR is already up: #220. It does what you describe:
GenerateParameters.seedset in both handlers, the globalMLXRandom.seedkept for the paths that callMLXRandom.categoricaldirectly, andUInt64(bitPattern: Int64(seed))for the conversion. The contract script got both assertions (same seed twice → identical message;seed: -1→ 200 and/healthstill answers), plus a unit test for the mapping. CI is green across the matrix.