{"record":{"id":"72fddbabcd5b1067","repo":"hiyouga/LlamaFactory","slug":"ep-size-must-be-positive-got-self-ep-size","errorCode":null,"errorMessage":"ep_size must be positive, got {self.ep_size}.","messagePattern":"ep_size must be positive, got (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/v1/plugins/trainer_plugins/distributed/interface.py","lineNumber":59,"sourceCode":"    dcp_path: str | None = None\n\n\n@dataclass\nclass FSDPTurboParams:\n    name: Literal[\"fsdpturbo\"] = \"fsdpturbo\"\n    reshard_after_forward: bool = True\n    offload_params: bool = False\n    pin_memory: bool = True\n    dcp_path: str | None = None\n    ep_size: int = 1\n    ep_dispatcher: str = \"eager\"\n    fsdp_ignored_modules: list[str] = field(default_factory=list)\n    hook_modules: list[str] = field(default_factory=list)\n    fsdp_implementation: str = \"native\"\n\n    def __post_init__(self) -> None:\n        if self.ep_size < 1:\n            raise ValueError(f\"ep_size must be positive, got {self.ep_size}.\")\n\n\n@dataclass\nclass DeepSpeedParams:\n    name: Literal[\"deepspeed\"] = \"deepspeed\"\n    config_file: str = \"\"\n\n    def __post_init__(self) -> None:\n        if not self.config_file:\n            raise ValueError(\"DeepSpeed config_file is required.\")\n\n\nclass DistributedPlugin(BasePlugin):\n    \"\"\"Plugin family for distributed training backends.\"\"\"\n\n\n@DistributedPlugin(\"fsdp2\").register()\nclass FSDP2Distributed(BaseDistributed):","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/v1/plugins/trainer_plugins/distributed/interface.py#L41-L77","documentation":"ValueError from FSDP2Params.__post_init__ (interface.py:59) validating that ep_size is at least 1. It fires at config-parse time, before any distributed setup, when a non-positive expert-parallel size is supplied. This is a strict guard against typo'd or computed-to-zero ep_size values.","triggerScenarios":"Passing ep_size: 0 or a negative value in the dist_config mapping for the fsdp2/fsdpturbo plugin; or computing ep_size programmatically (e.g. world_size // num_nodes) and passing 0 when the division underflows.","commonSituations":"YAML typo (ep_size: 0), templated configs where a variable evaluates to 0, or scripts that derive ep_size from environment variables defaulting to 0.","solutions":["Set ep_size to 1 (no expert parallelism) or a positive divisor of world size.","If the value is computed, validate it before constructing FSDP2Params and fall back to 1 when the computation yields <= 0.","Re-check the YAML/JSON config for a mistyped ep_size key value."],"exampleFix":"# before\nFSDP2Params(ep_size=world_size // 16)  # 0 when world_size < 16\n\n# after\nFSDP2Params(ep_size=max(1, world_size // 16))","handlingStrategy":"validation","validationCode":"assert isinstance(ep_size, int) and ep_size >= 1, f\"ep_size must be >= 1, got {ep_size}\"","typeGuard":"def is_valid_ep_size(v) -> bool:\n    return isinstance(v, int) and not isinstance(v, bool) and v >= 1","tryCatchPattern":null,"preventionTips":["Validate computed config values before building params dataclasses.","Use max(1, computed) for derived sizes."],"tags":["config","validation","distributed"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}