Troubleshooting

Beta
Last updated  Oct 5, 2026

Most problems come down to three things: the map, the adapter, or the voice setup. Start with the first checks, then find your symptom below.

First checks

  • Log every decision with onAction={(action) => console.log(action)}. A refused action has a reason and a detail that usually tell you exactly what’s wrong.
  • Pass voice.onDiagnostic and voice.onError and watch the console while you tap the orb.
  • Test your map without voice: remove the key and the voice prop, and type into the panel. The local mode uses the same map and validation chain.
  • Drive a turn from code with ref.current.sendText("take me to billing") and inspect the AgentAction it returns.

The orb only opens a chat panel

  • A malformed key doesn’t break the page: Usher logs a console error and runs text-only. Check the console, and check that the key starts with pk_test_ or pk_live_.
  • The key is undefined, so Usher is in local text-only mode (the panel status reads “Text mode”). Check that VITE_VOQAL_KEY or NEXT_PUBLIC_VOQAL_KEY is set in .env.local, then restart the dev server. Vite and next dev read that file only at startup.
  • In a deployed Next.js app, NEXT_PUBLIC_ values are inlined at build time. Set the variable in the environment and rebuild.

The orb doesn't appear

  • It’s fixed to the bottom-right at z-index 40. Check whether one of your overlays covers it.
  • In Next.js, make sure it renders from a client component. See Render it in the browser only.
  • Make sure <Usher> is mounted on the page you’re looking at. Mount it once in your signed-in layout, not inside one route.

Voice won't start

  • origin_not_allowed (403): your key is locked to certain domains, and this page’s origin isn’t one of them. Ask Voqal to add the exact origin (scheme, host, and port), or to remove the restriction. http://localhost and http://127.0.0.1 work on any port.
  • rate_limited (429): too many sessions for the key this minute. The panel says “Too many requests right now — please try again in a moment.”, and the status line reads “Too many requests — try again in a moment”. Wait and try again; if it happens in normal use, ask Voqal to raise your key’s limit.
  • invalid_key: the key is unknown or disabled, or it doesn’t start with pk_live_ or pk_test_.
  • It stops listening after a while: the microphone pauses after 30 seconds without recognised speech. Tap the orb to resume, or change idleTimeout.pauseMicAfterMs.
  • Permission denied: onError gets a NotAllowedError. Reset the site’s microphone permission in the browser. Chat still works through Aa.
  • Not HTTPS: browsers only allow the microphone on HTTPS or localhost.
  • Blocked socket: a CSP without the right connect-src host, or without blob: in script-src, stops the session or the microphone. See CSP.

It talks but nothing moves

  • It may have offered rather than navigated. That’s the design for “how do I” questions. Say “yes”, or say “take me to…”.
  • You may already be on that page. Usher never navigates to, or offers, the current page (on a dynamic page, the same record): nothing moves, no card appears, and onAction gets refused with already_here.
  • It was asked to do something rather than go somewhere (“book it for me”), and none of your tools fits. The assistant says it can’t and explains how to do it on the page. That’s deliberate: add a tool for that action.
  • Look for a refused action. not_in_allowlist means the id isn’t in the browser’s map.
  • navigation_failed means your adapter’s navigate threw. The detail holds your router’s error.
  • You added a page to destinations during a live session. An open session keeps the map it started with. End it and start a new one. See When prop changes apply.

It stopped replying

  • Every message gets a reply or an honest fallback. “Thinking…” stays until the reply starts. If the model says nothing, even after Usher nudges it, or nothing arrives within 20 seconds, the panel shows “Sorry — I didn’t catch that. Could you say a bit more?” and records turn:silent or fallback:shown.
  • If you see that line often, send Voqal the trace id from the console. The trace findings show which reliability events fired.
  • If the panel shows a connection or rate-limit line instead, see the entries on disconnects and voice errors.
  • A greeting or “ok” gets a reply, never a navigation. That’s deliberate.

It picks the wrong page

  • Give each important page a specific title and a one-line semanticDescription. That’s what the voice model reads.
  • Remove generic one-word aliases shared by sibling pages. Add the phrases users actually say.
  • Discovered-only routes get titles from their path (/settings/team becomes “team”). Add a manifest entry for anything users ask for by name.
  • The local ranker only matches ASCII letters and digits. Aliases in other scripts are ignored there, though the voice model still reads titles and descriptions in any language.

It keeps asking which one

  • For dynamic pages: a param_unresolved refusal means the id couldn’t be found. Pass context.selectedEntity on detail pages, or add resolveEntity.
  • For similar pages: two destinations score close together. Make their aliases and descriptions clearly different.

It doesn't use my knowledge base

  • Look for a console.warn saying entries didn’t fit. A live session carries at most 8,000 characters of whole entries, in the order you list them. Put the most important entries first, and keep each one to a sentence or two.
  • Knowledge loaded after the session started reaches the next session, not the open one.
  • In local text mode, an entry has to share at least 30% of the question’s words. Add triggerPhrases for how users ask.

It thinks I'm on another page

  • Your adapter’s currentLocation() is stale. In Next.js, keep the location in a ref and pass subscribe, as in the App Router example.
  • Without onLocationChange events, a live session isn’t told when the user moves by hand.

The conversation resets or disconnects

  • A changed assistantId or organizationId starts a new conversation. That’s deliberate.
  • Signing out with runtime().logout() ends the session and clears the panel, as intended.
  • × on the orb and End in the chat panel end the session and clear the transcript. A tap on the orb only pauses voice, and – in the panel only minimizes it.
  • Voice stays off after I minimize the panel. That’s deliberate: – always pauses voice and never turns it back on by itself. Tap the orb, or the panel’s voice button (“Turn on voice”), to talk again. While voice is off, the panel says “Voice replies off”: replies show as text and nothing is spoken.
  • “Reconnecting…” means the connection dropped (a network blip, or the voice provider’s periodic connection limit) and Usher is bringing it back. Messages typed meanwhile are held and sent automatically, and the new connection gets a recap of the recent chat. Nothing to do.
  • “Connection lost — send a message to reconnect” means reconnecting failed, and UsherSessionLostError reaches onError. A message still waiting shows “Please send that again.” The next message starts a new session that continues from a recap. Repeated losses point to the network or a Content Security Policy block; check the console.
  • “Paused — send a message to continue” is not an error: the idle timeout or the maximum length ended the session. The next message continues where it left off.
  • A session that ends on its own after a while (“Paused”) is the idle timeout (5 minutes without activity) or the maximum session length (10 minutes). Both are adjustable with idleTimeout. See Idle timeout & session limits.
  • A full page reload starts a new session. The conversation lives in memory for this beta.

Next.js

  • “useRouter only works in Client Components”: add "use client" to the file that renders <Usher>.

Getting help

During the beta, email team@voqal.ai with the trace id from the browser console of the affected page load (the line that starts [usher] trace tr_), the package versions, your router, and the AgentAction from onAction. The trace id lets us find that session’s diagnostics. Leave out tokens and user data. See Support & diagnostics.

© 2026 VoqalVoqal SDK & engine documentation