{"record":{"id":"ccb57e1148152960","repo":"hiyouga/LlamaFactory","slug":"drop-last-must-be-true","errorCode":null,"errorMessage":"Drop last must be True.","messagePattern":"Drop last must be True\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/llamafactory/v1/core/utils/batching.py","lineNumber":198,"sourceCode":"        self.drop_last = drop_last\n        self.seed = seed\n        self._warned_truncation = False  # warn once when dropping fully-truncated (zero-loss) samples\n        # TODO: support length and infinity\n        dp_size = DistributedInterface().get_world_size(Dim.DP)\n\n        if self.global_batch_size is None:\n            self.global_batch_size = dp_size * micro_batch_size\n            self.num_micro_batch = 1\n        elif self.global_batch_size % (dp_size * micro_batch_size) == 0:\n            self.num_micro_batch = global_batch_size // dp_size // micro_batch_size\n        else:\n            raise ValueError(\n                \"Global batch size must be divisible by DP size and micro batch size. \"\n                f\"Got {global_batch_size} % ({dp_size} * {micro_batch_size}) != 0.\"\n            )\n\n        if not self.drop_last:\n            raise ValueError(\"Drop last must be True.\")\n\n        self._batch_info: BatchInfo = {\n            \"micro_batch_size\": self.micro_batch_size,\n            \"num_micro_batch\": self.num_micro_batch,\n            \"cutoff_len\": self.cutoff_len,\n        }\n\n        self._init_data_provider()\n\n        self._is_resuming: bool = False\n        self._data_iter = iter(self._data_provider)\n        self._buffer = StatefulBuffer()\n\n        logger.info_rank0(\n            f\"Init unified data loader with global batch size {self.global_batch_size}, \"\n            f\"micro batch size {self.micro_batch_size}, \"\n            f\"num micro batch {self.num_micro_batch}, \"\n            f\"cutoff len {self.cutoff_len}, \"","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/v1/core/utils/batching.py#L180-L216","documentation":"The v1 batching scheduler is built around StatefulDistributedSampler with drop_last semantics: every batch must be exactly global_batch_size so checkpoint-resumable step accounting stays exact. drop_last=False would produce a ragged final batch and break stateful resume, so it is rejected outright rather than silently supported.","triggerScenarios":"Instantiating the v1 batching/scheduler class with drop_last=False. There is no code path that tolerates it — the check is immediate.","commonSituations":"Reusing a v0 DataLoader habit (v0 commonly runs drop_last=False) in v1 configs; wrapping the scheduler with a generic loader factory that defaults drop_last=False.","solutions":["Pass drop_last=True (the only supported mode in v1)","If losing tail samples matters, oversize/repeat the dataset or accept the drop — v1's stateful resume requires uniform batches","Stay on the v0 pipeline if you need partial final batches"],"exampleFix":"# before\nsched = Batching(global_batch_size=128, micro_batch_size=32, drop_last=False)\n\n# after\nsched = Batching(global_batch_size=128, micro_batch_size=32, drop_last=True)","handlingStrategy":"validation","validationCode":"assert drop_last is True, \"v1 batching requires drop_last=True\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat drop_last=True as fixed in v1 config templates","Do not port v0 DataLoader flags verbatim into v1 batching arguments"],"tags":["batching","config","dataloader","training"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}