jamiepine/voicebox · error · ValueError
No voice profile resolved. Pass `profile=` with a voice prof
Error message
No voice profile resolved. Pass `profile=` with a voice profile name or id, or set a default voice in Voicebox → Settings → MCP.
What it means
Raised by the voicebox.speak MCP tool when resolve_profile returns None. Resolution falls through three layers — explicit profile arg, MCPClientBinding.profile_id for the calling client, and CaptureSettings.default_playback_voice_id (global default) — and none of them yielded a VoiceProfile row.
Source
Thrown at backend/mcp_server/tools.py:79
prompt — when true, the text is first rewritten in character by the
LLM before TTS. When omitted, the per-client binding's
``default_personality`` flag decides; when that is unset, the
default is plain TTS.
``model_size`` selects a model variant for engines that ship more
than one — ``qwen`` and ``qwen_custom_voice`` accept "1.7B" (default)
or "0.6B"; ``tada`` accepts "1B" or "3B". Other engines ignore it.
Omit to use the engine default. Requesting a smaller variant (e.g.
"0.6B") is faster and avoids reloading a heavier model between calls.
"""
from ..database.models import MCPClientBinding
db = next(get_db())
try:
client_id = current_client_id.get()
vp = resolve_profile(profile, client_id, db)
if vp is None:
raise ValueError(
"No voice profile resolved. Pass `profile=` with a "
"voice profile name or id, or set a default voice in "
"Voicebox → Settings → MCP."
)
binding = None
if client_id:
binding = (
db.query(MCPClientBinding)
.filter(MCPClientBinding.client_id == client_id)
.first()
)
resolved_personality = personality
if resolved_personality is None and binding is not None:
resolved_personality = bool(binding.default_personality)
resolved_engine = engineView on GitHub (pinned to 51f49dea19)
Solutions
- Pass profile="<name-or-id>" explicitly on the tool call with an existing VoiceProfile.
- Open Voicebox → Settings → MCP and set a default playback voice (writes CaptureSettings.default_playback_voice_id).
- Bind the MCP client to a profile via MCPClientBinding (per-client override).
- Verify the profile still exists in the DB — a deleted profile name resolves to None.
Example fix
// before voicebox.speak(text="hello") // after voicebox.speak(text="hello", profile="Ryan")
Defensive patterns
Strategy: validation
Validate before calling
from backend.services.profiles import get_profile_orm_by_name_or_id
from backend.database import get_db
db = next(get_db())
if not profile_arg:
# ensure a default exists before calling speak
from backend.database.models import CaptureSettings
has_default = db.query(CaptureSettings).filter(CaptureSettings.id == 1).first()
if not has_default or not has_default.default_playback_voice_id:
raise RuntimeError("no default voice set; configure Voicebox → Settings → MCP")
elif get_profile_orm_by_name_or_id(profile_arg, db) is None:
raise ValueError(f"profile {profile_arg!r} not found")
# safe to call voicebox.speak Type guard
def profile_is_resolvable(profile_arg: str | None, client_id: str | None) -> bool:
from backend.mcp_server.resolve import resolve_profile
from backend.database import get_db
db = next(get_db())
return resolve_profile(profile_arg, client_id, db) is not None Try / catch
try:
await voicebox.speak(text=text, profile=profile_arg)
except ValueError as exc:
if "No voice profile resolved" in str(exc):
# set a fallback default or prompt the user to configure one
await voicebox.speak(text=text, profile="Ryan")
else:
raise Prevention
- During install/setup, always create a VoiceProfile and set CaptureSettings.default_playback_voice_id.
- Pass profile= explicitly from MCP clients rather than relying on resolution chains.
- Verify the profile name still exists before forwarding (deleted profiles resolve to None).
When it happens
Trigger: Calling voicebox.speak without profile= on a client that has no MCPClientBinding and no global default voice; passing a profile= name or id that does not exist in the DB; a fresh install where no default playback voice has been set.
Common situations: New deployment before any voice profile is configured; client_id changed so the per-client binding no longer matches; the named profile was deleted but a stale reference remains; the user expects an implicit default that was never created.
Related errors
- Unknown model size: {model_size}
- Unknown model size: {model_size}
- Unknown model size: {model_size}
- Pass exactly one of `audio_base64` or `audio_path`.
- `audio_path` is only available to loopback callers — remote
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/6e301fd7be1fbc0c.
Report an issue: GitHub.