Troubleshooting
This chapter is a symptom index. Find what you see, check the listed item, and follow the link to the chapter section that holds the fix.
Where to look first
Check that the gateway is up and read the licence and call counts:
curl -s http://127.0.0.1:8081/healthRead the last 200 log lines around the time of the problem. See Logs for where they live on a binary and on Docker.
If the complaint is "slow", read the per-turn LATENCY line. See Measuring response time. It needs
LOG_LATENCY=true.
No audio or one-way audio
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Call answers, nobody hears anything (SIP) | RTP blocked by a firewall, or the address in the SDP is a private one behind NAT | SIP_PUBLIC_ADDRESS is set to the public address; the RTP port range is open both ways |
Firewall, Gateway side |
| Caller hears the bot, bot never hears the caller | NAT or firewall closes the inbound RTP; codec mismatch on the trunk | Compare the codecs on the trunk with the gateway | When it does not work |
| No call reaches the gateway (AudioSocket) | Wrong port, or AUDIOSOCKET_HOST is 127.0.0.1 while Asterisk runs on another machine |
Port in the dialplan matches AUDIOSOCKET_PORT; the host is reachable from Asterisk |
Troubleshooting, Dialplan |
| Browser widget connects, no sound | Microphone blocked, or the origin or key was rejected | Close code and HTTP status in the browser console | Troubleshooting, Close codes |
Call connects, bot silent
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Caller hears the timeout message, or nothing, after speaking | The AI brain is unreachable or too slow | LITEBOT_API_URL or CUSTOM_LLM_URL answers from the gateway host |
When the AI is slow or fails |
| Caller hears the fallback greeting text only | The brain failed on the first turn | Log lines at the start of the call; the brain endpoint | Greeting, AI brain |
| Brain replies in the log, but no audio plays | The TTS provider key or voice was rejected, the turn fell to Google, and Google credentials are missing | Provider key and GOOGLE_APPLICATION_CREDENTIALS |
Choosing a TTS provider, Google |
| Caller hears an error sentence read aloud | The brain relayed its own error as the reply | The brain's own logs | When LiteBot relays an error |
Bot cuts itself off or talks over the caller
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Bot stops mid-sentence with nobody speaking | Its own voice leaks back from the handset and is taken as the caller | Log line BARGE-IN: echo transcript ignored |
Not hearing itself |
| Bot stops, then carries on from where it stopped | A false interruption was detected and playback resumed | Log line BARGE-IN: false interruption |
Interrupting the bot |
| Background noise or hold music interrupts the bot | Threshold too low for the line | The noise settings for the call | Noisy lines |
| Bot talks while the caller is still speaking | Caller speech arrived while the bot was busy | Log line STT: transcript held during |
Speech while the bot is busy, Held transcripts |
Caller's short replies ignored
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| A bare "yes" after the greeting gets no answer | First-turn speech gate in the recogniser | The transcript log line for that turn | Short replies and quiet handsets |
| Several short replies arrive as one late transcript | Handset stops sending audio while quiet, so the turn never closes | The inbound frame figures on the transcript log line | Short replies and quiet handsets |
| Reply spoken during a long answer is lost | Held and replayed, or dropped as echo or a stale acknowledgement | Log lines starting STT: for that turn |
Held transcripts |
| Noise-only turns never reach the bot | The transcript sanity gate dropped them | The gate settings | What never reaches the bot |
Wrong or no voice
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| One language is silent or in another voice, the rest are fine (Google) | The Google voice does not match the language, Google answers 400, and the turn fails over | GOOGLE_VOICE_<LANG> for that language; a global voice name set for all languages |
|
| Sarvam turns sound like the Google voice | The speaker id is not valid for the chosen model, Sarvam answers 400, and the cascade ends on Google | SARVAM_TTS_MODEL, SARVAM_TTS_SPEAKER and SARVAM_TTS_SPEAKER_<LANG> |
Sarvam |
| One fixed phrase (greeting, goodbye) is always in the wrong voice | A fallback render was cached under the wrong key | Whether the phrase was ever spoken during a provider outage | Purge that entry: The TTS cache, TTS cache API |
| Per-call voice or model is ignored | tts_provider for the call names a different provider than the voice belongs to |
The call's session metadata | Per-call voice and model, Session metadata |
Clicks or gaps between sentences
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Sentence endings clipped, words missing at the tail | Sarvam bulbul:v4-flash over the WebSocket stream drops the sentence tail |
SARVAM_TTS_STREAM_TRANSPORT |
Set it to http and purge cached Sarvam audio: Sarvam, The TTS cache |
| Sentences run together, or a click at the join | Seam padding and edge fades off or too small | TTS_SENTENCE_MIN_GAP_MS, TTS_SENTENCE_EDGE_FADE_MS |
Sentence streaming and seams |
| Pauses between sentences of a long answer | Synthesis slower than playback; log line WARN: TTS starvation sentence N |
Provider and voice speed; how often the line appears | Sentence streaming and seams, Tuning that is safe to touch |
| Audio stutters across every call at once | The host froze; log line LOOP-LAG: <n>ms stall ended |
Host load, virtual machine pauses | Logs |
Replies slow
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Long wait before every answer | The brain, or its tool calls, take long | The llm column of the LATENCY line | Measuring response time, When the AI is slow or fails |
| Wait grows only on turns that use tools | Several hops inside the brain | The llm column on tool turns against plain turns | AI brain |
| Audio starts late after the text is ready | The TTS provider is slow to first audio | The tts column | Choosing a TTS provider |
| Wait between the caller stopping and the bot starting | Endpointing or a handset that stops sending audio | The buffer and stt columns | Short replies and quiet handsets, Choosing a recogniser |
Call never hangs up
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| The bot says goodbye but the caller's line stays open | The gateway's hang-up message is lost in NAT on a long call | Whether the PBX trunk sends keepalive options to the gateway (Asterisk-front trunk only; leave it off on a direct-bridge trunk) | Customer PBX side, Connect your PBX to the trunk |
| Goodbye is cut off or the line drops late | Audio is drained before teardown, up to a ceiling | LLM_HANGUP_DRAIN_MAX_MS |
Ending the call |
Active call count in /health stays above zero |
Calls that did not end cleanly | The PBX call list against the gateway count | Health and licence, Active calls and stats |
Transfers fail
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
ERROR: Transfer requested on a SIP call but only an HTTP TRANSFER_ENDPOINT is configured — set TRANSFER_SIP_URI (e.g. sip:7000@pbx) for REFER |
A SIP call needs a SIP target; the HTTP endpoint serves AudioSocket only | TRANSFER_SIP_URI starts with sip: |
Transfer to a person |
| AudioSocket call asks for a person, nothing happens | No endpoint, or the call's channel was never sent | TRANSFER_ENDPOINT; the channel field in the session metadata |
Transfers, Send caller details first |
| Bot never offers a transfer | Transfers switched off, or the brain never raises the flag | TRANSFER_ENABLED=true; the brain's escalation flag |
Transfer to a person, Flags |
| The PBX refuses the transfer | The target extension does not exist for the trunk context | PBX log for the REFER | Customer PBX side |
Webhooks missing
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Nothing arrives at the receiver | WEBHOOK_URL unset, or the receiver is unreachable |
Log line WARN: Lifecycle webhook [<event>] failed |
Lifecycle webhooks |
| Receiver on a private network never gets events | The address check blocks private targets | WEBHOOK_SSRF_CHECK |
Delivery rules |
| An event was lost once and never came again | Webhooks are not retried | Receiver availability at that moment | Delivery rules |
| Signature never matches | The receiver re-serialised the body before hashing | Hash the raw body bytes | Verifying the signature |
Billing records stuck in pending or dead
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
| Records pile up under the pending folder | BILLING_CDR_URL empty (store-only), unreachable, or answering with an error |
The URL, the auth settings and the receiver's status code | Delivery and retries |
| Records in the dead folder | The retry ladder was exhausted, or the receiver returned a client error | The receiver's reply for one record | Delivery and retries |
| Receiver fixed, old records still waiting | Dead records need an explicit replay | /health pending and dead counts |
Replaying records, Billing replay |
Licence invalid after container recreate
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
Falling back to FREE tier (4 concurrent calls) at boot |
The key does not match this machine: hostname or CPU count changed, for example after a container recreate with a new hostname | GET /license; the hostname inside the container |
Pin hostname: in the compose file and ask Vexyl for a key for the new machine: Licence inside Docker, Licence |
SIP call rejected due to concurrent limit: N/M |
The licence call cap is reached | concurrentCalls in /health |
Health and licence, Capacity |
AUTH: Blocked request from <ip> for <METHOD> <path> |
The caller's address is not in HTTP_ALLOWED_IPS |
The allow-list | Access |
Process restarts
| What you see | Likely cause | Check this | Fix |
|---|---|---|---|
WARN: RTP port <n> in use by another process (...), retrying on a fresh port (<attempt>) |
The gateway RTP range overlaps the one a co-hosted Asterisk uses | RTP_PORT_MIN, RTP_PORT_MAX against Asterisk's rtpstart and rtpend |
Do not let the RTP ranges overlap, Co-hosting Asterisk and the gateway |
| Container exits at once or loops | A mounted folder the process cannot write to | The container logs | Logs |
| Process restarts under pm2 or systemd | Crash; the last lines before the restart name the cause | The last 200 log lines | Logs, Run |
What to send Vexyl
When you ask for help, send all of this in one message:
- The time of the call, with its timezone.
- The caller's number.
- The DID that was called.
- What the caller heard.
- A log excerpt covering the call, a few lines either side.
- The output of the health check shown at the top of this chapter.
Hosted customers: see Getting help.