unslothai/unsloth · error · HTTPException

Caption too long (max {_MAX_CAPTION_CHARS} characters).

Error message

Caption too long (max {_MAX_CAPTION_CHARS} characters).

What it means

HTTP 400 from the caption-update route: after stripping, the submitted caption exceeds _MAX_CAPTION_CHARS = 2000 characters. The limit keeps .txt sidecars (which the trainer reads per image) bounded. Blank captions are allowed and mean 'clear'; anything over 2000 chars is rejected before any file write.

Source

Thrown at studio/backend/routes/training.py:3867

    response_model = DiffusionDatasetImageRecord,
)
async def set_diffusion_dataset_caption(
    name: str,
    filename: str,
    body: DiffusionCaptionUpdateRequest,
    current_subject: str = Depends(get_current_subject),
    _interlock: None = Depends(diffusion_dataset_interlock),
):
    """Write (or, when blank, clear) an image's ``.txt`` caption sidecar. Returns the
    updated image record."""
    _require_diffusion_dataset_mutable()
    folder = _resolve_dataset_folder(name)
    image_path = _safe_dataset_image_path(folder, filename)
    if not image_path.is_file():
        raise HTTPException(status_code = 404, detail = "Image not found.")
    caption = (body.caption or "").strip()
    if len(caption) > _MAX_CAPTION_CHARS:
        raise HTTPException(
            status_code = 400,
            detail = f"Caption too long (max {_MAX_CAPTION_CHARS} characters).",
        )

    def write() -> DiffusionDatasetImageRecord:
        sidecar = image_path.with_suffix(".txt")
        if caption:
            sidecar.write_text(caption, encoding = "utf-8")
            image_path.with_suffix(".caption").unlink(missing_ok = True)
            return _image_record(folder, image_path, _load_metadata_captions(folder))
        # Blank must actually clear. Unlinking alone would resurface this image's metadata caption,
        # so write an EMPTY sidecar: reader and trainer treat it as an authoritative tombstone.
        meta = _load_metadata_captions(folder)
        try:
            rel = image_path.relative_to(folder).as_posix()
        except ValueError:
            rel = image_path.name
        if image_path.name in meta or rel in meta:

View on GitHub (pinned to 203007d190)

Solutions

  1. Shorten the caption to <=2000 characters.
  2. For auto-caption pipelines, truncate per-caption output: caption[:2000].
  3. Prefer tag-style captions (comma-separated tags) over prose; trainer prompt handles tags better anyway.

Example fix

# before
api.set_caption(name, fname, huge_text)  # 20000 chars -> 400
# after
MAX = 2000
api.set_caption(name, fname, huge_text.strip()[:MAX])
Defensive patterns

Strategy: validation

Validate before calling

MAX_CAPTION_CHARS = 2000

def caption_ok(text: str | None) -> bool:
    return len((text or '').strip()) <= MAX_CAPTION_CHARS

Type guard

def is_caption_too_long(exc: HTTPException) -> bool:
    return exc.status_code == 400 and 'Caption too long' in str(exc.detail)

Try / catch

try:
    await api.set_caption(name, fname, cap)
except HTTPStatusError as e:
    if e.response.status_code == 400 and 'Caption too long' in e.response.text:
        await api.set_caption(name, fname, cap.strip()[:2000])
    else:
        raise

Prevention

When it happens

Trigger: POST a caption update whose body.caption.strip() length > 2000 — e.g. pasting a long story/essay, a whole article, or accidentally a base64 blob into the caption field.

Common situations: Pasting article-length text into the labeling UI; auto-captioning pipelines that concatenate many tag lists; prompt templates accidentally injected wholesale; multibyte content is counted by characters, not bytes.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/e57123190cf1e414. Report an issue: GitHub.