jamiepine/voicebox · error · ValueError

`audio_path` must be absolute.

Error message

`audio_path` must be absolute.

What it means

Raised by voicebox_transcribe when audio_path is supplied but Path(audio_path).is_absolute() is False. Only absolute paths are accepted so the resolved file cannot be confused by the server's current working directory.

Source

Thrown at backend/mcp_server/tools.py:145

        model: str | None = None,
    ) -> dict[str, Any]:
        if bool(audio_base64) == bool(audio_path):
            raise ValueError(
                "Pass exactly one of `audio_base64` or `audio_path`."
            )

        # Absolute-path mode: validate and transcribe in place. Restricted
        # to loopback callers so a Voicebox bound on 0.0.0.0 doesn't double
        # as an unauthenticated arbitrary-local-file read primitive.
        if audio_path is not None:
            if not request_is_loopback():
                raise ValueError(
                    "`audio_path` is only available to loopback callers — "
                    "remote callers must use `audio_base64`."
                )
            path = Path(audio_path)
            if not path.is_absolute():
                raise ValueError("`audio_path` must be absolute.")
            if not path.is_file():
                raise ValueError(f"File not found: {audio_path}")
            if path.stat().st_size > MAX_TRANSCRIBE_BYTES:
                raise ValueError(
                    f"File exceeds {MAX_TRANSCRIBE_BYTES // (1024 * 1024)} MB limit."
                )
            return await _transcribe_file(path, language, model)

        # Base64 mode: decode into a temp file, transcribe, clean up.
        try:
            raw = b64.b64decode(audio_base64, validate=True)
        except Exception as exc:
            raise ValueError(f"Invalid audio_base64: {exc}") from exc
        if len(raw) > MAX_TRANSCRIBE_BYTES:
            raise ValueError(
                f"Audio exceeds {MAX_TRANSCRIBE_BYTES // (1024 * 1024)} MB limit."
            )
        with tempfile.NamedTemporaryFile(

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Pass a fully resolved absolute path, e.g. str(Path(...).resolve()).
  2. Expand user and symlinks on the caller side before sending: Path(p).expanduser().resolve().
  3. Ensure the path uses the server's OS conventions (forward slashes on Linux).

Example fix

// before
voicebox_transcribe(audio_path="clips/hello.wav")
// after
voicebox_transcribe(audio_path=str(pathlib.Path("clips/hello.wav").resolve()))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
p = Path(audio_path).expanduser()
if not p.is_absolute():
    p = p.resolve()
await voicebox_transcribe(audio_path=str(p))

Type guard

def is_absolute_audio_path(value: str) -> bool:
    from pathlib import Path
    return isinstance(value, str) and Path(value).is_absolute()

Try / catch

try:
    await voicebox_transcribe(audio_path=audio_path)
except ValueError as exc:
    if "must be absolute" in str(exc):
        from pathlib import Path
        await voicebox_transcribe(audio_path=str(Path(audio_path).resolve()))
    else:
        raise

Prevention

When it happens

Trigger: Passing a relative path like "audio/clip.wav" or "./clip.wav" as audio_path; a client joining a directory to a filename with a leading './'.

Common situations: Client building paths with os.path.join from a relative base; shell-style '~' not expanded (also not absolute); Windows-style drive paths on a Linux server.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/cdc0f5af456aceb2. Report an issue: GitHub.