unslothai/unsloth · error · ValueError

That reference file carries no audio track.

Error message

That reference file carries no audio track.

What it means

decode_h3_reference_audio opens an uploaded audio reference with PyAV and raises ValueError when container.streams.audio is empty — the uploaded file has no audio track. Structural first check, before any decode/resample work, mirroring the video path's no-video-track guard.

Source

Thrown at studio/backend/core/inference/video_minimax_h3.py:444

    frames = frames[: int(round(duration * H3_FPS))]

    waveform, sample_rate = (None, None)
    with av.open(io.BytesIO(blob)) as container:
        if container.streams.audio:
            waveform, sample_rate = _decode_audio_stream(container, np)
    return frames, waveform, sample_rate


def decode_h3_reference_audio(blob: bytes) -> tuple[Any, int]:
    """Decode one uploaded audio file to a float32 ``(samples, channels)`` waveform + its rate."""
    import io

    import av
    import numpy as np

    with av.open(io.BytesIO(blob)) as container:
        if not container.streams.audio:
            raise ValueError("That reference file carries no audio track.")
        waveform, sample_rate = _decode_audio_stream(container, np)
    if waveform is None:
        raise ValueError("That reference audio decoded to no samples.")
    return waveform, sample_rate


def _decode_audio_stream(container: Any, np: Any) -> tuple[Optional[Any], Optional[int]]:
    """The container's first audio stream as float32 ``(samples, channels)`` at its own rate.

    Bounded while decoding, for the reason the video path above is: the encoded size says almost
    nothing about the decoded size. A 32 MiB request-limit MP3 is over half an hour of audio, which
    expands to ~1.9 GB of float32 here and doubles again in ``np.concatenate``, and three
    references are accepted per request. H3's reference window is
    ``H3_REF_VIDEO_MAX_SECONDS`` anyway, so anything past it is unusable rather than merely large:
    refuse it with the same message the video guard uses instead of decoding it first."""
    import av

    stream = container.streams.audio[0]

View on GitHub (pinned to 203007d190)

Solutions

  1. Upload a real audio file (wav/mp3/m4a/flac) to the audio-reference field.
  2. If using a video as the audio source, confirm it has an audio track first (ffprobe).
  3. Validate streams client-side before submitting.

Example fix

# before: silent video uploaded as audio reference -> ValueError
upload_audio_reference(blob=silent_video_bytes)

# after
import av, io
with av.open(io.BytesIO(blob)) as c:
    if not c.streams.audio:
        raise ValueError("file carries no audio; pick a real audio track")
Defensive patterns

Strategy: validation

Validate before calling

import av, io

def has_audio_track(blob: bytes) -> bool:
    with av.open(io.BytesIO(blob)) as c:
        return bool(c.streams.audio)

Try / catch

try:
    wf, sr = decode_h3_reference_audio(blob)
except ValueError as e:
    if "no audio track" in str(e):
        return HTTPException(400, "upload an audio file for the audio reference")
    raise

Prevention

When it happens

Trigger: Uploading a video file to the reference-audio field when it has no audio track (silent video); uploading an image or document; uploading a MIDI or other non-audio-container file.

Common situations: Users assuming any video upload carries sound (screen recordings often do not); field mix-ups between the audio and video reference uploaders; muted exports.

Related errors


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