SDKsWeb SDK

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 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.exclude intentionally 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 programmatic location.assign there 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]);

Next steps

On this page