Skip to content

Troubleshooting

Installation found the wrong vaani

The public PyPI project named vaani is unrelated. Delete that environment and follow the private installation. Use the private index with first-index; do not use a bare pip install vaani.

Artifact Registry authentication fails

Check:

  1. gcloud auth application-default login completed for the intended identity;
  2. the identity has Artifact Registry Reader access;
  3. keyring and keyrings.google-artifactregistry-auth are installed;
  4. UV_KEYRING_PROVIDER=subprocess is set;
  5. the index username is the fixed value oauth2accesstoken.

Do not paste an access token into a URL, shell history, image, or log.

./scripts/check reports missing credentials

Copy .env.example to .env, then fill every variable required by the selected preset. A Google service-account variable contains the complete JSON value unless the preset explicitly declares a file-based variable.

Changing the preset also requires its extras and a regenerated production lock.

The worker does not register

Confirm:

  • LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET are correct;
  • the agent name matches the token/dispatch configuration;
  • ./scripts/check passes;
  • ./scripts/dev shows the worker registration before the browser connects;
  • the selected preset's provider extras are installed.

Browser connection, SIP routing, and agent dispatch are separate. A registered worker does not prove that a trunk routes to it.

The worker reports CERTIFICATE_VERIFY_FAILED

Opening the LiveKit URL in a browser only proves that the browser trusts its HTTPS certificate. The worker uses Python aiohttp to open a secure WebSocket at /agent, which may use a different CA bundle. Incognito mode does not test the Python path.

From the generated project root, run the same unauthenticated TLS handshake as the worker. Replace the hostname with the one in LIVEKIT_URL:

LIVEKIT_TEST_URL="wss://your-project.livekit.cloud/agent" \
  backend/.venv/bin/python - <<'PY'
import asyncio
import os

import aiohttp


async def check() -> None:
    try:
        async with aiohttp.ClientSession() as session:
            await session.ws_connect(os.environ["LIVEKIT_TEST_URL"])
    except aiohttp.WSServerHandshakeError as error:
        print(f"TLS_OK HTTP_{error.status}")
    except Exception as error:
        print(f"TLS_FAILED {type(error).__name__}: {error}")


asyncio.run(check())
PY

TLS_OK HTTP_401 is expected: the probe has no LiveKit credentials, but TLS verification succeeded. If it prints TLS_FAILED, retry development with the CA bundle installed in the generated environment:

SSL_CERT_FILE="$(backend/.venv/bin/python -m certifi)" ./scripts/dev

If that still fails, a company proxy, antivirus product, or network gateway may be replacing the public certificate. Use the CA bundle approved by that network's administrator. Never disable certificate verification.

The bot does not greet first

Check that the Assistant has a non-empty greeting, the preparation hook returns before its deadline, and the worker receives the participant. If the greeting uses customer data, test the customer lookup independently and handle an unknown caller explicitly.

The greeting does not use the customer's name

The name must come from @assistant.prepare and the greeting must read it from call.state:

@assistant.prepare
async def load_customer(call: Call) -> object:
    customer = await crm.find_by_phone(call.caller)
    return {"name": customer.name}


def greeting_for(call: Call) -> str:
    return f"Namaste {call.state['name']} ji."

Also confirm that browser/SIP identity supplies the expected caller number and that test-mode redaction is not replacing prepared state.

Responses are slow

Measure end-of-speech → final STT, final STT → first model text, and first model text → first TTS audio separately.

Then verify:

  • the runtime is the current approved streaming build;
  • the selected preset uses its intended STT mode: batch for basic-monolingual@1, streaming for birla-prod-v42@14;
  • LLM output and TTS are streaming;
  • Gemini thinking level, token limits, endpointing, and VAD match the reviewed preset;
  • provider clients are reused instead of constructed per turn;
  • providers and LiveKit run in the intended region;
  • the prompt does not ask for long answers.

Do not implement a second streaming loop in the bot. Fix shared pipeline latency in VAANI or its preset.

Live transcript is empty

Confirm participant audio reaches the worker and that the frontend subscribes to LiveKit transcription text streams. The VAANI worker publishes both participant and assistant transcript segments. Check worker errors before changing frontend rendering.

If audio works but text does not, ensure the frontend and VAANI runtime are on compatible LiveKit versions and the transcript panel is listening for lk.transcription.

Speech is not understood

Confirm the preset's language, Deepgram model, and active language binding. Test with a clean microphone and inspect the participant transcript. If the transcript is wrong, diagnose STT/VAD/noise settings; if it is right but the answer is wrong, diagnose the prompt, model, or tools.

A prompt or language switch fails

switch_prompt requires the target name to be registered with assistant.add_prompt. switch_language requires a complete preset language binding. Regenerate the production lock after either definition changes.

A transfer fails

Register a safe route name with assistant.add_transfer_route, configure its deployment-owned destination, and test it against an authorized SIP target. The model never receives the raw destination.

FrozenInstanceError

VAANI configuration values are immutable. Construct a new value or use dataclasses.replace; do not assign fields after construction. An Assistant also freezes after successful check() or call start, so register prompts, tools, routes, Judge configuration, and overrides first.

InvalidCallSetup or lock mismatch

Read error.issues. Common causes are:

  • changed preset without regenerating the lock;
  • missing provider extra or credential;
  • unsupported language/voice override;
  • tool without complete annotations;
  • named prompt or transfer route added after the lock was created.

Fix the definition, regenerate the lock, review its diff, and rerun ./scripts/check.

Next: live voice testing or production readiness.