jamiepine/voicebox · error · ValueError
Pass exactly one of `audio_base64` or `audio_path`.
Error message
Pass exactly one of `audio_base64` or `audio_path`.
What it means
Raised by voicebox_transcribe when both audio_base64 and audio_path are passed, or when neither is. The check is bool(audio_base64) == bool(audio_path), so exactly one input channel must be truthy: base64 bytes for any caller, or an absolute local file path restricted to loopback callers.
Source
Thrown at backend/mcp_server/tools.py:130
finally:
db.close()
@mcp.tool(
name="voicebox.transcribe",
description=(
"Transcribe an audio clip to text using Voicebox's local Whisper. "
"Pass exactly one of `audio_base64` (bytes as base64) or "
"`audio_path` (absolute local file path — loopback callers only)."
),
)
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:View on GitHub (pinned to 51f49dea19)
Solutions
- Supply exactly one of audio_base64 or audio_path with non-empty content.
- For remote callers, always use audio_base64.
- For loopback/local tooling, prefer audio_path to avoid the base64 encode/decode round trip.
Example fix
// before voicebox_transcribe(audio_base64=b64, audio_path="/tmp/a.wav") // after voicebox_transcribe(audio_base64=b64)
Defensive patterns
Strategy: validation
Validate before calling
has_b64 = bool(audio_base64)
has_path = bool(audio_path)
if has_b64 == has_path:
raise ValueError("Supply exactly one of audio_base64 or audio_path.")
await voicebox_transcribe(audio_base64=audio_base64, audio_path=audio_path) Type guard
def has_exactly_one_audio_source(b64: str | None, path: str | None) -> bool:
return bool(b64) != bool(path) Try / catch
try:
await voicebox_transcribe(audio_base64=audio_base64, audio_path=audio_path)
except ValueError as exc:
if "exactly one of" in str(exc):
# pick the channel you actually have and retry
if audio_base64:
await voicebox_transcribe(audio_base64=audio_base64)
elif audio_path:
await voicebox_transcribe(audio_path=audio_path)
else:
raise Prevention
- Use mutually exclusive parameters at the client API (one non-null) rather than two optionals.
- Treat empty string as 'not supplied' explicitly before calling.
- Document that base64 is the universal channel and audio_path is loopback-only.
When it happens
Trigger: Calling voicebox_transcribe() with no arguments; passing both audio_base64 and audio_path; passing an empty string for one and a real value for the other (empty is falsy and counts as 'not supplied').
Common situations: Client defaults both params to None and forgets to set one; pipeline wires base64 and path simultaneously 'just in case'; an empty/blank base64 string treated as 'set'.
Related errors
- `audio_path` must be absolute.
- Invalid audio_base64: {exc}
- Invalid STT model '{model_size}'. Must be one of: {', '.join
- `audio_path` is only available to loopback callers — remote
- File not found: {audio_path}
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/f78127436c0278e9.
Report an issue: GitHub.