hiyouga/LlamaFactory · error · ValueError

More {segment} tags than provided media files.

Error message

More {segment} tags than provided media files.

What it means

During multimodal sample conversion, each inline media placeholder tag (e.g. <image>, <video>, <audio>) in the text consumes one path from the sample's media column. This ValueError fires when the text contains more placeholder tags of a modality than the sample provides media files for that modality, so a tag has no path to bind.

Source

Thrown at src/llamafactory/v1/plugins/data_plugins/converter.py:111

def _to_content_blocks(text: str, media_iters: dict[str, Any]) -> list[Content]:
    """Split ``text`` on inline media placeholders, interleaving media-url content blocks.

    Each placeholder consumes the next path from its modality iterator (in document order). Plain
    text with no placeholders yields a single text block (byte-identical to the legacy behavior).
    Raises on an unmatched placeholder (more tags than media files).
    """
    if not _TAG_PATTERN.search(text):
        return [{"type": "text", "value": text}]

    blocks: list[Content] = []
    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:

View on GitHub (pinned to f28afaf635)

Solutions

  1. Fix the offending rows: make the number of media tags in the text exactly equal the length of the corresponding images/videos/audios list.
  2. Audit the dataset with a small script counting tags vs list lengths per sample to find bad rows.
  3. Remove stray placeholder tags from text if the media was never intended.
  4. If a single sample uses one media file repeated, duplicate the path in the media list.

Example fix

// before
{"instruction": "Describe <image> and <image>", "output": "...", "images": ["a.jpg"]}

// after
{"instruction": "Describe <image> and <image>", "output": "...", "images": ["a.jpg", "b.jpg"]}
Defensive patterns

Strategy: validation

Validate before calling

import json, re
from llamafactory.extras.constants import IMAGE_PLACEHOLDER, VIDEO_PLACEHOLDER, AUDIO_PLACEHOLDER

TAGS = {'images': IMAGE_PLACEHOLDER, 'videos': VIDEO_PLACEHOLDER, 'audios': AUDIO_PLACEHOLDER}

def sample_ok(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

assert all(sample_ok(json.loads(l)) for l in open('train.jsonl'))

Type guard

def has_matching_tag_counts(row: dict) -> bool:
    """True when every media tag in text has a corresponding media file."""
    return sample_ok(row)

Try / catch

try:
    sample = converter(raw)
except ValueError as e:
    if 'tags than provided media' in str(e):
        logger.error('bad row skipped: %s', raw.get('id', '?'))
        continue
    raise

Prevention

When it happens

Trigger: A dataset row whose instruction/output text contains N '<image>' tags but whose 'images' field has fewer than N entries (same for videos/audios). Raised from _to_content_blocks during DataConverterPlugin conversion (alpaca/sharegpt converters).

Common situations: Hand-edited JSONL where an image tag was added but the images array not updated; templates that inject an extra media placeholder; OCR/caption datasets with inconsistent tag counts per row.

Related errors


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