hiyouga/LlamaFactory · error · ValueError
Fewer {tag} tags than provided media files ({unused} unused)
Error message
Fewer {tag} tags than provided media files ({unused} unused). What it means
The mirror check of error 343: after converting a sample, the converter asserts every provided media file was consumed by a placeholder tag. If the images/videos/audios list is longer than the number of corresponding tags in the text, the unused count is reported and conversion fails.
Source
Thrown at src/llamafactory/v1/plugins/data_plugins/converter.py:123
for segment in _TAG_PATTERN.split(text):
block_type = _TAG_TO_BLOCK.get(segment)
if block_type is not None:
try:
path = next(media_iters[segment])
except StopIteration:
raise ValueError(f"More {segment} tags than provided media files.") from None
blocks.append({"type": block_type, "value": path})
elif segment:
blocks.append({"type": "text", "value": segment})
return blocks
def _assert_media_consumed(media_iters: dict[str, Any]) -> None:
"""Ensure every media file was referenced by a tag (fewer tags than media -> error)."""
for tag, media_iter in media_iters.items():
unused = len(list(media_iter))
if unused:
raise ValueError(f"Fewer {tag} tags than provided media files ({unused} unused).")
class DataConverterPlugin(BasePlugin):
"""Plugin for data converters."""
def __call__(self, raw_sample: dict[str, Any]) -> Sample:
return super().__call__(raw_sample)
@DataConverterPlugin("alpaca").register()
def alpaca_converter(raw_sample: AlpacaSample) -> SFTSample:
"""Convert Alpaca sample to SFT sample.
See raw example at: https://huggingface.co/datasets/llamafactory/alpaca_gpt4_en
Args:
raw_sample (AlpacaSample): Alpaca sample.
View on GitHub (pinned to f28afaf635)
Solutions
- Make tag counts and media list lengths match exactly per sample per modality.
- Add the missing placeholder tags into the text (e.g. one <image> per image).
- Trim extra unused entries from the media arrays.
- Write a pre-flight validator that fails fast on the whole file before training starts.
Example fix
// before
{"instruction": "Describe this", "images": ["a.jpg", "b.jpg"]}
// after
{"instruction": "Describe <image> and <image>", "images": ["a.jpg", "b.jpg"]} Defensive patterns
Strategy: validation
Validate before calling
def counts_match(row) -> bool:
text = ' '.join(str(row.get(k, '')) for k in ('instruction', 'input', 'output', 'conversations'))
for col, tag in TAGS.items():
media = row.get(col) or []
media = media if isinstance(media, list) else [media]
if len(re.findall(re.escape(tag), text)) != len(media):
return False
return True Type guard
def counts_match(row: dict) -> bool:
"""Exact 1:1 media tag <-> media file correspondence for all modalities."""
... Try / catch
try:
converter(raw)
except ValueError as e:
if 'Fewer' in str(e):
row.setdefault('images', [])[:] = row['images'][:used] # or log & drop row
raise Prevention
- Use exact equality checks (not just >=) when validating multimodal rows.
- Prefer generating text tags from media lists in preprocessing scripts.
- Fail the whole dataset check in CI, not at training time row-by-row.
When it happens
Trigger: A dataset row with more entries in 'images'/'videos'/'audios' than the corresponding placeholder tags appearing in the concatenated conversation text.
Common situations: Datasets ported from other formats where the media array keeps extra files; deleting a tag from the prompt but not the media list; single-tag conventions (one <image> tag, multiple images) applied to v1, which requires exact 1:1 correspondence.
Related errors
- More {segment} tags than provided media files.
- Invalid input type {input_item.type}.
- Unknown dataset filetype: {filetype}.
- DPO training dataset is empty: {dataset_path}
- DPO training requires pair-format samples containing chosen/
AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14).
Data as JSON: /api/errors/3eeb3a68e9175c45.
Report an issue: GitHub.