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

  1. Always pass profile (name or id) in the request body when no default is configured.
  2. Configure a default profile in Voicebox → Settings → MCP for the given client.
  3. Ensure the X-Voicebox-Client-Id header is sent so the binding lookup can resolve a default.
  4. 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

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.