jamiepine/voicebox · error · ValueError

Invalid audio_base64: {exc}

Error message

Invalid audio_base64: {exc}

What it means

Raised by voicebox_transcribe when b64.b64decode(audio_base64, validate=True) throws — i.e. audio_base64 is not legal base64. The original exception is chained (from exc) and surfaced as 'Invalid audio_base64: <reason>'.

Source

Thrown at backend/mcp_server/tools.py:158

                    "`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(
            suffix=".wav", delete=False
        ) as tmp:
            tmp.write(raw)
            tmp_path = Path(tmp.name)
        try:
            return await _transcribe_file(tmp_path, language, model)
        finally:
            tmp_path.unlink(missing_ok=True)

    @mcp.tool(
        name="voicebox.list_captures",
        description=(
            "List recent voice captures (dictations, recordings, uploads) "

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Encode the bytes with standard base64 on the caller side: base64.b64encode(data).decode().
  2. Strip any 'data:...;base64,' prefix before sending.
  3. Ensure correct padding (= or ==) and the standard (+/) alphabet, or pre-convert from urlsafe.
  4. Send the whole payload — avoid truncation by transport limits.

Example fix

// before
voicebox_transcribe(audio_base64=raw_bytes_str)
// after
voicebox_transcribe(audio_base64=base64.b64encode(raw_bytes).decode('ascii'))
Defensive patterns

Strategy: validation

Validate before calling

import base64
# will raise here if invalid, instead of inside the tool
raw = base64.b64decode(audio_base64, validate=True)
await voicebox_transcribe(audio_base64=audio_base64)

Type guard

def is_valid_base64(value: str | None) -> bool:
    import base64
    if not isinstance(value, str) or not value:
        return False
    try:
        base64.b64decode(value, validate=True)
        return True
    except Exception:
        return False

Try / catch

try:
    await voicebox_transcribe(audio_base64=audio_base64)
except ValueError as exc:
    if "Invalid audio_base64" in str(exc):
        # strip data: URI prefix / re-pad, then retry
        clean = audio_base64.split(",", 1)[-1]
        clean += "=" * (-len(clean) % 4)
        await voicebox_transcribe(audio_base64=clean)
    else:
        raise

Prevention

When it happens

Trigger: Sending raw bytes instead of base64; a data-URI prefix ('data:audio/wav;base64,...') left on the string; wrong alphabet (URL-safe vs standard); truncated payload; non-ASCII characters or whitespace in the middle of the string.

Common situations: Frontend forgetting to base64-encode a Blob; copy-paste truncation; padding ('=') stripped; using base64.urlsafe_b64encode on the client but standard decode on the server.

Related errors


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