unslothai/unsloth · error · SttAudioTooLongError

Audio must be {max_minutes} {unit} or shorter.

Error message

Audio must be {max_minutes} {unit} or shorter.

What it means

Raised during frame-by-frame decoding when the cumulative resampled sample count exceeds _MAX_AUDIO_SECONDS * _TARGET_SAMPLE_RATE, i.e. the clip is longer than the hard cap (shown in whole minutes). The cap is enforced as frames arrive so long uploads are rejected early rather than after a full decode.

Source

Thrown at studio/backend/core/inference/stt_sidecar.py:1100

    sample_count = 0
    raw_buffer = io.BytesIO()
    resampler = av.audio.resampler.AudioResampler(
        format = "s16",
        layout = "mono",
        rate = _TARGET_SAMPLE_RATE,
    )
    # Group frames before resampling so short clips need one resampler call
    # rather than one per codec frame.
    fifo = av.audio.fifo.AudioFifo()

    def write_frame(frame) -> None:
        nonlocal sample_count
        array = frame.to_ndarray()
        sample_count += array.size
        if sample_count > max_samples:
            max_minutes = _MAX_AUDIO_SECONDS // 60
            unit = "minute" if max_minutes == 1 else "minutes"
            raise SttAudioTooLongError(f"Audio must be {max_minutes} {unit} or shorter.")
        raw_buffer.write(array)

    try:
        with av.open(io.BytesIO(audio), mode = "r", metadata_errors = "ignore") as container:
            if not container.streams.audio:
                raise SttAudioDecodeError("Could not decode the audio.")
            frames = iter(container.decode(audio = 0))
            while True:
                try:
                    frame = next(frames)
                except StopIteration:
                    break
                except InvalidDataError:
                    # Skip a corrupt frame rather than fail the whole transcription.
                    continue
                if cancel_event is not None and cancel_event.is_set():
                    raise SttTranscriptionCancelledError("Transcription cancelled.")
                frame.pts = None

View on GitHub (pinned to 203007d190)

Solutions

  1. Trim or split the audio client-side into segments within the cap (the engine windows at 30s internally, but the total input cap still applies).
  2. If self-hosting and longer input is acceptable, raise _MAX_AUDIO_SECONDS consciously — it exists to bound memory/CPU of decode.
  3. Show the limit in the upload UI so users pre-split long files.

Example fix

// before
stt.transcribe(hour_long_mp3);  // SttAudioTooLongError
// after
for chunk in split_audio(hour_long_mp3, max_seconds=(_MAX_AUDIO_SECONDS - 5)):
    results.append(stt.transcribe(chunk))
Defensive patterns

Strategy: validation

Validate before calling

MAX_S = _MAX_AUDIO_SECONDS  # import from stt_sidecar
if estimate_duration_seconds(audio_path) > MAX_S - 5:  # small safety margin
    split_or_reject(audio_path)

Try / catch

try:
    text = stt.transcribe(audio)
except SttAudioTooLongError as e:
    show_user(str(e)); suggest_splitting()

Prevention

When it happens

Trigger: Uploading audio longer than the cap (e.g. >10 min at 16 kHz if _MAX_AUDIO_SECONDS is 600) to transcribe(); the count is checked inside write_frame after each resampled batch, so any decode whose cumulative samples exceed max_samples raises.

Common situations: Users dictating or uploading long recordings (meetings, podcasts); clients that concatenate clips before sending; changed constants after an upgrade lowering the cap.

Related errors


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