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

  1. Make tag counts and media list lengths match exactly per sample per modality.
  2. Add the missing placeholder tags into the text (e.g. one <image> per image).
  3. Trim extra unused entries from the media arrays.
  4. 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

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


AI-assisted analysis of hiyouga/LlamaFactory@f28afaf635 (2026-08-14). Data as JSON: /api/errors/3eeb3a68e9175c45. Report an issue: GitHub.