jamiepine/voicebox · error · HTTPException
No voice profile resolved. Pass `profile` (name or id), or c
Error message
No voice profile resolved. Pass `profile` (name or id), or configure a default in Voicebox → Settings → MCP.
What it means
Returned by POST /speak when resolve_profile returns None AND data.profile is empty/None (HTTP 400). The handler treats 'no identifier supplied and no default resolvable' as a client error distinct from a not-found. The message directs the user to either pass profile or set an MCP default in settings.
Source
Thrown at backend/routes/speak.py:47
data: models.SpeakRequest,
request: Request,
db: Session = Depends(get_db),
):
"""Speak text in a voice profile. Mirrors voicebox.speak (MCP).
Response shape matches POST /generate — a ``GenerationResponse`` with
``status="generating"`` and an ``id`` the caller polls at
``GET /generate/{id}/status``.
"""
client_id = request.headers.get("X-Voicebox-Client-Id")
profile = resolve_profile(data.profile, client_id, db)
if profile is None:
if data.profile:
raise HTTPException(
status_code=404,
detail=f"Voice profile '{data.profile}' not found.",
)
raise HTTPException(
status_code=400,
detail=(
"No voice profile resolved. Pass `profile` (name or id), "
"or configure a default in Voicebox → Settings → MCP."
),
)
binding = None
if client_id:
binding = (
db.query(MCPClientBinding)
.filter(MCPClientBinding.client_id == client_id)
.first()
)
# Resolve per-client personality default when the caller didn't pin it.
personality_flag = data.personality
if personality_flag is None and binding is not None:View on GitHub (pinned to 51f49dea19)
Solutions
- Always pass profile (name or id) in the request body when no default is configured.
- Configure a default profile in Voicebox → Settings → MCP for the given client.
- Ensure the X-Voicebox-Client-Id header is sent so the binding lookup can resolve a default.
- In the client, detect 'no profile' state and prompt selection before calling /speak.
Example fix
// before
{ "text": "hi" } // profile omitted, no default
// after
{ "text": "hi", "profile": "default-narrator" } Defensive patterns
Strategy: validation
Validate before calling
function speakPayloadOk(profile, clientId) {
if (profile) return true;
return !!clientId; // default binding only resolvable when client_id is sent
} Type guard
function hasSpeakTarget(profile, clientId) { return !!profile || !!clientId; } Try / catch
try { await api.post('/speak', { text }); }
catch (e) { if (e.response?.status === 400) promptForProfile(); throw e; } Prevention
- Always send profile when no MCP default is configured.
- Include the X-Voicebox-Client-Id header so a default binding can resolve.
- Configure a default profile in Settings → MCP for each client.
When it happens
Trigger: POST /speak with profile omitted/null and no MCP default configured for the client. First-time use before any default is set, or after the default binding was cleared.
Common situations: New Voicebox client that has not configured a default profile. Client_id not sent (no X-Voicebox-Client-Id header) so no binding could be looked up, and profile omitted.
Related errors
AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12).
Data as JSON: /api/errors/5bb97d3def80b1c4.
Report an issue: GitHub.