What happened
Qwen3.8-2.4T-A95B UD-Q1_0 (370 GiB, 10 shards) loads to a serving state on --device cuda on a DGX Spark (GB10, 119 GB unified memory) and then dies on the first request:
=== READY after ~1560s === (26 min, so the load itself succeeded)
=== measuring (4 tokens) ===
TOTAL 0 tokens in 402.8s
TEXT: ''
== CUDA == CUDA Version 13.0.1
engine-fatal: EngineCore busy loop threw: vt cuda: cudaMalloc: out of memory
async-llm: output handler saw engine death: EngineCore encountered an issue [vt cuda: cudaMalloc: out of memory]
sse: stream aborted mid-flight: ...
The failure is in the EngineCore busy loop, not the loader — so weight residency is fine and it is the forward that cannot allocate.
Why this matters
The same checkpoint serves correctly on --device cpu on the same box, same binary, same prompt: TTFT 667.0 s, steady decode 44.2 s/token, coherent output. So the capability exists on one device path and not the other, and the GPU path is where the compute headroom is.
The plausible mechanism, not yet confirmed: the disk-borrow residency that makes this fit is a CPU-path capability. HostExpertSlotStore is host memory by construction, and the expert-streaming lane is CPU-gated. On the CUDA path the forward presumably needs expert slices (and activations) in device allocations, and cudaMalloc draws from the same unified 119 GB pool that already holds the dense remainder plus page cache. 2790 slices/token at 2.38 MB is ~6.5 GB per token of expert bytes that would have to be staged. Someone should confirm which allocation actually fails and its size rather than trust that reasoning — the log line does not name it.
Related: keep-quant device slice needs ResidentWeight is a known constraint in this area.
Reproduction
Under an rc lease on dgx:gpu0 (rc hold dgx:gpu0 --ttl 3h, then ssh — the checkpoint is on the host/NAS and rc run gives the container only):
docker run -d --name q1cuda --gpus all \
-v /home/mudler/iq1bench:/src -v /home/mudler/ckpt:/ckpt:ro -w /src \
-e VT_GGUF_PREFAULT=0 \
vllmcpp-build:gb10 ./build-srv/examples/vllm-server \
--model /ckpt/qwen3.8-q1_0/Qwen3.8-2.4T-A95B-UD-Q1_0-00001-of-00010.gguf \
--device cuda --max-num-seqs 1 --max-model-len 512 --port 8899
Checkpoint: unsloth/Qwen3.8-2.4T-A95B-GGUF at revision 567d3e6ac26c5474b18311e619c04350fb9a5556, also staged at /usr/local/nas_share/checkpoints/qwen3.8-2.4t-a95b-ud-q1_0.
Readiness must be probed with python3, not curl — that image has no curl, and a suppressed curl exit 127 reads as "not ready" forever.
What would close this
Either the forward allocates within the pool on a larger-than-memory GGUF, or the engine refuses --device cuda for this configuration by name at load time with a message saying why. Loading for 26 minutes and then dying mid-stream is the worst of the three outcomes, and per policy an unimplemented arm should refuse with a message naming the missing part rather than fail late.
Not a blocker for the CPU result
The measured CPU numbers stand and are unaffected. Recording this so the --device cuda gap is visible debt rather than something rediscovered by the next person who tries the obvious thing.
What happened
Qwen3.8-2.4T-A95B UD-Q1_0(370 GiB, 10 shards) loads to a serving state on--device cudaon a DGX Spark (GB10, 119 GB unified memory) and then dies on the first request:The failure is in the EngineCore busy loop, not the loader — so weight residency is fine and it is the forward that cannot allocate.
Why this matters
The same checkpoint serves correctly on
--device cpuon the same box, same binary, same prompt: TTFT 667.0 s, steady decode 44.2 s/token, coherent output. So the capability exists on one device path and not the other, and the GPU path is where the compute headroom is.The plausible mechanism, not yet confirmed: the disk-borrow residency that makes this fit is a CPU-path capability.
HostExpertSlotStoreis host memory by construction, and the expert-streaming lane is CPU-gated. On the CUDA path the forward presumably needs expert slices (and activations) in device allocations, andcudaMallocdraws from the same unified 119 GB pool that already holds the dense remainder plus page cache. 2790 slices/token at 2.38 MB is ~6.5 GB per token of expert bytes that would have to be staged. Someone should confirm which allocation actually fails and its size rather than trust that reasoning — the log line does not name it.Related:
keep-quant device slice needs ResidentWeightis a known constraint in this area.Reproduction
Under an
rclease ondgx:gpu0(rc hold dgx:gpu0 --ttl 3h, then ssh — the checkpoint is on the host/NAS andrc rungives the container only):Checkpoint:
unsloth/Qwen3.8-2.4T-A95B-GGUFat revision567d3e6ac26c5474b18311e619c04350fb9a5556, also staged at/usr/local/nas_share/checkpoints/qwen3.8-2.4t-a95b-ud-q1_0.Readiness must be probed with
python3, notcurl— that image has no curl, and a suppressedcurlexit 127 reads as "not ready" forever.What would close this
Either the forward allocates within the pool on a larger-than-memory GGUF, or the engine refuses
--device cudafor this configuration by name at load time with a message saying why. Loading for 26 minutes and then dying mid-stream is the worst of the three outcomes, and per policy an unimplemented arm should refuse with a message naming the missing part rather than fail late.Not a blocker for the CPU result
The measured CPU numbers stand and are unaffected. Recording this so the
--device cudagap is visible debt rather than something rediscovered by the next person who tries the obvious thing.