Troubleshooting
Common Web SDK issues, debug mode, and performance notes
Common problems when integrating the Web SDK and how to resolve them. For channel- and account-level issues (a session that won't connect, no audio, the avatar not loading), see the troubleshooting guide for the API and the WebRTC channel page.
Common issues
Module errors, tokens, styling, mounting
Debug mode
Turn on verbose logging
Performance
Bundle size and memory
Common issues
"Module not found"
Make sure the package and its peer dependency are both installed:
npm install @touchcastllc/napster-companion-api @reduxjs/toolkit@reduxjs/toolkit is a required peer dependency of the ESM build. See Installation.
Invalid or expired token
Tokens are short-lived. If init rejects with a token error:
- Mint a fresh token from your backend right before connecting.
- Make sure you pass the token as the first argument to
init(token, config)and don't decode or modify it.
Microphone access is blocked
If the user denies microphone permission (or the browser blocks it), the SDK shows built-in guidance walking the user through enabling microphone access in their browser settings. For iframe-embedded integrations, also make sure your embedding frame grants the permission — see the API troubleshooting guide.
The avatar appears unstyled
The stylesheet isn't loaded. In the ESM build you must import it once:
import "@touchcastllc/napster-companion-api/styles";Or load it via a <link> tag — see Installation. The standalone build bundles the styles, so this doesn't apply there.
The mount container doesn't exist
Initialize the SDK only after the container is in the DOM. In plain HTML, wait for the document to be ready:
document.addEventListener("DOMContentLoaded", async () => {
await NapsterCompanionApiSdk.init(token, {
mountContainer: "#avatar-container",
});
});If the selector matches no element, the SDK warns and falls back to document.body. In React, Vue, and Angular, the framework examples mount inside lifecycle hooks that guarantee the element exists.
"SDK is already initialized"
init throws if a session is already running. Call destroy() on the previous instance before initializing a new one.
The session ends when the user navigates
A full page load tears down the session. To keep one session alive across pages, enable persistence — and note its rules:
- The destination must be same-origin. Cross-origin links always break out and end the session.
- URLs listed in
persistence.excludeintentionally break out. - Under
persistence.iframeOnNavigate: true, the landing page stays unframed until the first link click — and while unframed, only anchor clicks are intercepted, so a programmaticlocation.assignthere ends the session unless you dispatch the persist-navigate event instead. The default (wrap on connect) doesn't have this window.
Debug mode
Set debug: true to get verbose SDK logging in the console. Pair it with onError and onData to see what's flowing:
await NapsterCompanionApiSdk.init(token, {
debug: true,
onError: error => console.error("SDK error:", error),
onData: data => console.log("SDK data:", data),
});Performance
Bundle size
- Use the ESM build rather than the standalone bundle so your bundler can tree-shake unused code.
- Import only the features you use so your bundler can drop the rest.
Memory
Always call destroy() when the widget unmounts — it closes the connection and releases the microphone (plus any screen-share or face-tracking camera capture, if used). In component frameworks, call it from the unmount hook:
useEffect(() => {
return () => {
instance?.destroy();
};
}, [instance]);