unslothai/unsloth · error · ValueError
No captioned images found. Provide a metadata.jsonl / captio
Error message
No captioned images found. Provide a metadata.jsonl / captions.jsonl, per-image .txt captions, or an instance prompt.
What it means
After scanning metadata.jsonl/captions.jsonl, per-image .txt sidecars, and the dreambooth instance_prompt fallback, zero image/caption pairs were produced. Every image lacked a caption from every source, or the directory had no recognized image files at all, so the trainer refuses to start with an empty dataset.
Source
Thrown at studio/backend/core/training/diffusion_train_common.py:1451
# 3. dreambooth instance prompt for any image still without a caption.
if not caption and instance_prompt:
caption = instance_prompt
if caption:
if verify_images:
# Reject a corrupt/truncated image now via a cheap PIL header probe: otherwise it passes filename-only discovery, the start route frees the GPU models, and the trainer crashes in Image.open.
try:
from PIL import Image
with Image.open(img) as _probe:
_probe.verify()
except Exception as e: # noqa: BLE001 -- corrupt/zero-byte/truncated file
raise ValueError(
f"Image cannot be decoded: {img.name} ({e}). Remove or replace the "
f"corrupt or zero-byte file before training."
) from e
pairs.append((str(img), caption))
if not pairs:
raise ValueError(
"No captioned images found. Provide a metadata.jsonl / captions.jsonl, per-image "
".txt captions, or an instance prompt."
)
return pairs
# Families whose trainer has no checkpoint/resume support yet. The shared DiffusionLoraConfig
# carries save_steps / resume_from_checkpoint for every family, so a loop that implements
# neither has to say so rather than ignore them.
CHECKPOINTLESS_FAMILIES: frozenset[str] = frozenset({"minimax-h3"})
# Families whose forward covers ONE packed sequence, so the batch axis is a pure replication
# axis and a second clip cannot join it: the layout, the rotary grid and the row timesteps are
# set by that clip's geometry and its caption's length. Kept beside the refusal it explains.
SINGLE_SEQUENCE_FAMILIES: frozenset[str] = frozenset({"minimax-h3"})
# Families whose trainer loads its base through ``ModularPipeline.from_pretrained``. Their local
# layout is ``modular_model_index.json`` and no ``model_index.json``, so the conventional shapeView on GitHub (pinned to 203007d190)
Solutions
- Add a metadata.jsonl (or captions.jsonl) with {"file_name": "img.png", "prompt": "..."} records.
- Or add a per-image caption sidecar: img.png + img.txt in the same directory.
- Or set an instance_prompt on the run so every uncaptioned image falls back to it.
- Verify image extensions are within the recognized set and metadata file_name values exactly match the image filenames.
Example fix
# before: data_dir has only images
# after: add captions.jsonl in data_dir
{"file_name": "img_0001.png", "prompt": "a photo of sks dog"}
{"file_name": "img_0002.png", "prompt": "a photo of sks dog at the beach"} Defensive patterns
Strategy: validation
Validate before calling
IMAGE_EXTS = {'.png', '.jpg', '.jpeg', '.webp', '.bmp'}
def dataset_is_captionable(root: Path, instance_prompt=None) -> bool:
imgs = [p for p in root.iterdir() if p.is_file() and p.suffix.lower() in IMAGE_EXTS]
if not imgs:
return False
has_meta = (root / 'metadata.jsonl').is_file() or (root / 'captions.jsonl').is_file()
has_sidecars = any(p.with_suffix('.txt').is_file() for p in imgs)
return bool(has_meta or has_sidecars or instance_prompt) Try / catch
try:
pairs = build_caption_pairs(...)
except ValueError as e:
if 'No captioned images' in str(e):
prompt_user_for_captions_or_instance_prompt() Prevention
- Ship a dataset linter that reports caption coverage per image before training.
- Standardize on metadata.jsonl with file_name fields that exactly match filenames (case and extension).
When it happens
Trigger: A data_dir with images but no metadata.jsonl, no sidecar .txt files, and no instance_prompt; or a data_dir whose files use an extension outside _IMAGE_EXTS; or a metadata.jsonl whose keys don't match the images (bad-upload records are skipped, not fatal).
Common situations: User uploads bare images expecting automatic captioning; sidecar files named image-caption.txt instead of image.txt; metadata.jsonl file_name fields with wrong paths/extensions; forgetting to set instance_prompt for dreambooth-style datasets.
Related errors
- No captioned video clips found. MiniMax-H3 trains from clips
- Unsupported local dataset format: {all_files[0]}
- dataset_slice_end must be greater than or equal to dataset_s
- hf_dataset is too long (max 256 chars)
- hf_dataset contains invalid characters or path segments
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/55cec5ea07630d03.
Report an issue: GitHub.