jamiepine/voicebox · error · ValueError

`audio_path` is only available to loopback callers — remote

Error message

`audio_path` is only available to loopback callers — remote callers must use `audio_base64`.

What it means

Raised by voicebox_transcribe when audio_path is supplied and request_is_loopback() is False. The audio_path mode reads arbitrary local files, so it is deliberately restricted to loopback (same-host) callers to prevent a Voicebox bound on 0.0.0.0 from becoming an unauthenticated arbitrary-local-file read primitive.

Source

Thrown at backend/mcp_server/tools.py:139

        ),
    )
    async def voicebox_transcribe(
        audio_base64: str | None = None,
        audio_path: str | None = None,
        language: str | None = None,
        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:

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Switch the remote caller to audio_base64 (works from anywhere).
  2. Run the file-path caller on the same host as Voicebox and connect via 127.0.0.1/localhost.
  3. Do NOT widen this guard — it is intentional; instead encode the file as base64 remotely.

Example fix

// before (remote caller)
voicebox_transcribe(audio_path="/var/data/clip.wav")
// after
voicebox_transcribe(audio_base64=base64.b64encode(open("/var/data/clip.wav","rb").read()).decode())
Defensive patterns

Strategy: validation

Validate before calling

from backend.mcp_server.context import request_is_loopback

if audio_path and not request_is_loopback():
    # encode and send as base64 instead
    import base64
    audio_base64 = base64.b64encode(open(audio_path, "rb").read()).decode()
    audio_path = None
await voicebox_transcribe(audio_base64=audio_base64, audio_path=audio_path)

Type guard

def can_use_audio_path() -> bool:
    from backend.mcp_server.context import request_is_loopback
    return request_is_loopback()

Try / catch

try:
    await voicebox_transcribe(audio_path=audio_path)
except ValueError as exc:
    if "loopback callers" in str(exc):
        import base64
        await voicebox_transcribe(
            audio_base64=base64.b64encode(open(audio_path, "rb").read()).decode()
        )
    else:
        raise

Prevention

When it happens

Trigger: A remote MCP client (request arriving over a non-loopback interface) calling voicebox_transcribe(audio_path=...); running Voicebox on 0.0.0.0 or behind a reverse proxy that does not preserve the loopback origin.

Common situations: Exposing Voicebox to LAN/public while still trying to use file-path mode; proxy/TLS terminator rewriting the source address so the request no longer looks loopback; mixing local and remote clients against one instance.

Related errors


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