{"record":{"id":"52e4b698208c7c4b","repo":"jax-ml/jax","slug":"num-threads-and-thread-name-must-be-either-both-se","errorCode":null,"errorMessage":"num_threads and thread_name must be either both set or both None, got {self}","messagePattern":"num_threads and thread_name must be either both set or both None, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"jax/_src/pallas/mosaic_gpu/core.py","lineNumber":1650,"sourceCode":"  cluster_names: Sequence[str] = ()\n  # Those are NOT CUDA threads. On Hopper they correspond to warpgroups.\n  num_threads: int | None = None\n  thread_name: str | None = None\n  kernel_name: str | None = None\n\n  def __post_init__(self):\n    if len(self.cluster) > 3:\n      raise ValueError(f\"cluster= must be at most 3D, got {self}.\")\n    if len(self.grid_names) != len(self.grid):\n      raise ValueError(\n          f\"grid_names must have the same length as grid, got {self}.\"\n      )\n    if len(self.cluster_names) != len(self.cluster):\n      raise ValueError(\n          f\"cluster_names must have the same length as cluster, got {self}.\"\n      )\n    if (self.thread_name is None) != (self.num_threads is None):\n      raise ValueError(\n          \"num_threads and thread_name must be either both set or both None,\"\n          f\" got {self}\"\n      )\n    max_mosaic_threads = 2048 // 128\n    if self.num_threads is not None and self.num_threads > max_mosaic_threads:\n      raise ValueError(\n          \"Requested too many CUDA threads per block. Each Mosaic thread\"\n          f\" corresponds to 128 CUDA threads. At most {max_mosaic_threads}\"\n          f\" are supported, got {self}\"\n      )\n    object.__setattr__(self, \"grid\", tuple(self.grid))\n    object.__setattr__(self, \"grid_names\", tuple(self.grid_names))\n    object.__setattr__(self, \"cluster\", tuple(self.cluster))\n    object.__setattr__(self, \"cluster_names\", tuple(self.cluster_names))\n\n  @property\n  def default_memory_space(self) -> MemorySpace:\n    return MemorySpace.GMEM","sourceCodeStart":1632,"sourceCodeEnd":1668,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/pallas/mosaic_gpu/core.py#L1632-L1668","documentation":"Launch-config validation: `num_threads` and `thread_name` are an optional pair and must be set together or both left as None. Setting only one would create a named warp/thread axis with no size (or vice versa), so the dataclass rejects it.","triggerScenarios":"Constructing the launch config with num_threads=4 but thread_name=None, or thread_name='warp' without num_threads.","commonSituations":"Enabling warp specialization by adding num_threads but forgetting the name used by warp-specialization checks inside the kernel; refactoring away thread names while leaving num_threads behind.","solutions":["Set both: num_threads=4, thread_name='warp'","Or remove both to disable explicit threading"],"exampleFix":"// before\nnum_threads=4\nthread_name=None\n\n// after\nnum_threads=4\nthread_name='warp'","handlingStrategy":"validation","validationCode":"assert (num_threads is None) == (thread_name is None), 'set both or neither'","typeGuard":"def threading_pair_valid(num_threads, thread_name) -> bool:\n    return (num_threads is None) == (thread_name is None)","tryCatchPattern":"try:\n    cfg = LaunchConfig(num_threads=n, thread_name=t)\nexcept ValueError:\n    if n is not None and t is None:\n        t = 'warp'\n    cfg = LaunchConfig(num_threads=n, thread_name=t)","preventionTips":["Treat num_threads/thread_name as one setting; set them together in a helper","Disable both when not using warp specialization"],"tags":["jax","pallas","mosaic-gpu","warp-specialization","validation"],"backgroundTag":"paired-config-fields-required","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}