{"record":{"id":"712e24e3bae040e3","repo":"sgl-project/sglang","slug":"tokenizer-worker-num-must-be-positive","errorCode":null,"errorMessage":"tokenizer_worker_num must be positive","messagePattern":"tokenizer_worker_num must be positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/transport/cuda_ipc.py","lineNumber":44,"sourceCode":"    \"_sglang_defer_cuda_ipc_feature_reconstruction\"\n)\n\n\ndef get_mm_feature_pool_size_per_worker(\n    total_pool_size: int, tokenizer_worker_num: int\n) -> int:\n    \"\"\"Split the CUDA IPC feature-pool budget without exceeding it.\n\n    Each tokenizer worker owns a distinct CUDA allocation, even though all pools\n    are created on ``base_gpu_id``.  Therefore a minimum per-worker allocation\n    would make the aggregate HBM reservation larger than the configured budget.\n    Keep the configured value as a hard per-node cap and leave at most\n    ``tokenizer_worker_num - 1`` bytes unused when it is not evenly divisible.\n    \"\"\"\n    if total_pool_size <= 0:\n        raise ValueError(\"total_pool_size must be positive\")\n    if tokenizer_worker_num <= 0:\n        raise ValueError(\"tokenizer_worker_num must be positive\")\n\n    return total_pool_size // tokenizer_worker_num\n\n\n# Cache for pool-level IPC handles on the consumer side.\n# Key: the pool CUDA IPC handle tuple. Value: opened UntypedStorage.\n_pool_storage_cache: dict = {}\n_pool_cache_lock = threading.Lock()\n\n\ndef _normalize_pool_cache_key(pool_handle, device_index: int) -> tuple[Any, ...]:\n    normalized_handle = (\n        pool_handle if isinstance(pool_handle, tuple) else tuple(pool_handle)\n    )\n    return (device_index, normalized_handle)\n\n\ndef _open_pooled_storage_uncached(pool_handle):","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/transport/cuda_ipc.py#L26-L62","documentation":"The same pool-budget divider requires tokenizer_worker_num to be a positive integer: the per-worker budget is total_pool_size // tokenizer_worker_num, and zero/negative workers makes the division meaningless. Raised before any IPC handle is created.","triggerScenarios":"Initializing the CUDA IPC multimodal transport with tokenizer_worker_num <= 0, e.g. runtime parallel state not yet initialized returning 0, or a test passing 0 explicitly (test_rejects_invalid_budget_or_worker_count).","commonSituations":"Calling transport constructors before the scheduler/parallel runtime has set the tokenizer worker count; misconfigured --tokenizer-worker-num of 0; race during early startup.","solutions":["Ensure tokenizer worker count is set (>=1) before constructing the IPC transport","Fix --tokenizer-worker-num / dp-size configuration so at least one tokenizer worker exists","In tests, pass an explicit worker_num >= 1"],"exampleFix":"# before\nget_mm_feature_pool_size_per_worker(pool_bytes, tokenizer_worker_num=0)\n# after\nget_mm_feature_pool_size_per_worker(pool_bytes, tokenizer_worker_num=4)","handlingStrategy":"validation","validationCode":"assert isinstance(tokenizer_worker_num, int) and tokenizer_worker_num >= 1","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Initialize parallel/runtime state before constructing transports","Validate server-arg-derived counts at startup"],"tags":["cuda-ipc","memory-pool","config-validation","multimodal-transport"],"backgroundTag":"invalid-resource-size","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}