Hey, we're hitting a pretty bad issue with `useKri...
# ask-ai
c
Hey, we're hitting a pretty bad issue with
useKrispNoiseFilter
on LiveKit Cloud. Krisp works fine for a while, then randomly
createNoiseFilter
starts throwing
WORKLET_NOT_SUPPORTED
and it never recovers, the user has to restart the app. The bigger problem is that once it fails, it creates an infinite retry loop. The hook catches the error and sets
isNoiseFilterEnabled = false
, which naturally makes our code try to re-enable it, but
setNoiseFilterEnabled(true)
always creates a new processor (the callback has
[]
deps so the closure is stale on
krispProcessor
) and that new processor also fails, so it loops forever spamming
WORKLET_NOT_SUPPORTED
/
DOMException: The user aborted a request
. We also ran into a separate crash during room reconnections,
processor.restart()
throws inside
setMediaStreamTrack
but there's no catch, only a finally. So
sender.replaceTrack()
never runs and the mic is just dead. Do you have any ideas on what causes
createNoiseFilter
to randomly start failing mid-session? This is on Electron/Chromium desktop, happens on multiple machines, no pattern we can identify
✅ 1
t
Thanks for reaching out, Daniele. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — this sounds frustrating, and I appreciate the clear details you shared. I’m responding to the two problems you described: (1)
createNoiseFilter
randomly throwing
WORKLET_NOT_SUPPORTED
mid-session and causing an infinite retry loop, and (2)
processor.restart()
throwing during reconnections so
sender.replaceTrack()
never runs and the mic stays dead. What I can confirm •
WORKLET_NOT_SUPPORTED
from
createNoiseFilter
can happen on Electron/Chromium desktop and may appear mid-session; when it happens the noise filter doesn't automatically recover and a full app restart is needed. • There is a defect where failed
createNoiseFilter
attempts can lead to an infinite retry loop: the hook sets
isNoiseFilterEnabled = false
on error, your code tries to re-enable it,
setNoiseFilterEnabled(true)
creates a new processor (the closure is stale on
krispProcessor
) and that new processor also fails — so it repeats and spams
WORKLET_NOT_SUPPORTED
/
DOMException: The user aborted a request
. • During room reconnections,
processor.restart()
may throw inside
setMediaStreamTrack
. Because there’s no
catch
(only a
finally
),
sender.replaceTrack()
does not run and the mic remains dead. Possible causes and what to try now • Worklet lifecycle / environment issue in Chromium/Electron: ◦ A worklet can become unusable mid-session if the underlying AudioWorkletGlobalScope or its supporting resources are torn down or blocked by the renderer process. Electron’s Chromium version, renderer restarts, or other native modules could cause this. ◦ Conflicting native integrations or other audio-related components in the app could interfere with the worklet environment. • Immediate mitigations you can implement client-side (safe to try now): a. Prevent infinite retries with backoff and caps • When
createNoiseFilter
fails, record the failure and do not immediately re-enable. Use exponential backoff plus a max retry count (for example: 1s, 2s, 4s, then stop and surface a user-visible error or a “Retry” button). • This prevents the loop and reduces error spam while you gather more info. a. Make processor lifecycle explicit and avoid relying on stale closures • Ensure the hook or code that calls
setNoiseFilterEnabled(true)
references the latest
krispProcessor
instance (e.g., keep the processor ref in a stable ref or state that updates, avoid
[]
deps for callbacks that need fresh references). • On failure, fully dispose the failed processor (stop tracks, disconnect any worklet ports) before attempting a new creation. a. Robust error handling around restart/replaceTrack • Wrap
processor.restart()
in try/catch. In the catch, attempt to recover by: ◦ disposing the processor, ◦ calling
sender.replaceTrack()
with the plain mic track (so audio still flows), ◦ then re-create the processor only when stable. • Always ensure
sender.replaceTrack()
runs in a finally or after you’ve handled thrown errors so the mic isn’t left dead. a. Add telemetry/logging • Capture timestamps, Chromium/Electron version, renderer process restarts, and exact error stacks when
WORKLET_NOT_SUPPORTED
appears. This helps correlate the failures with environment events. a. Fallback path for users • Provide a fallback “disable noise suppression” option that stops trying to create the processor and continues with raw mic audio until a manual retry. Information you can collect (these will help narrow root cause) • Exact Electron and Chromium versions where the issue reproduces. • Whether renderer process crashes/reloads or extension/native modules are present when the error happens. • Full error stack traces for
WORKLET_NOT_SUPPORTED
and
DOMException: The user aborted a request
. • Whether the problem ever resolves without a full app restart (you said it does not — confirm). • A minimal reproduction (if possible) that toggles the noise filter repeatedly and logs the worklet state. Short-term recommended plan 1. Add defensive handling now: ◦ Exponential backoff + max retries when
createNoiseFilter
fails. ◦ Catch around
processor.restart()
and guarantee
sender.replaceTrack()
runs to avoid dead mic. ◦ Make processor instance management explicit to avoid stale closures. 1. Collect the diagnostics above to help identify environmental triggers (Electron version, renderer events, other native modules). 2. If you can produce a minimal repro, share logs and versions so we can look for a pattern or a bug we can reproduce. If you’d like, I can: • Walk through a suggested code patch to add backoff + lifecycle fixes and show exactly where to add try/catch and processor disposal. • Help draft the logging/telemetry you should add to capture the needed fields. • Review a minimal repro or paste the relevant parts of your hook implementation so I can point out where the stale closure and missing catch should be changed. Which would you prefer to do next, and can you share the Electron/Chromium versions and the hook code that manages
krispProcessor
? Sources: [Flutter iOS] SoundWaveformWidget / AudioVisualizerWidget Not Working (Miss..., with interactive elements
l
Hey there, out of interest, have you already tried ai-coustics audio enhancement as well, or only Krisp?
t
Thanks — good question. I haven’t tried ai-coustics here. LiveKit’s documented audio enhancement integration references Krisp for real‑time noise suppression; there’s no mention of ai‑coustics or any official support/experience with it in the materials I can access. If you’re interested in exploring ai‑coustics as an alternative, I can help in a few ways: • Walk through how to plug a different processor into your existing hook (what lifecycle points to manage, where to call replaceTrack, and how to guard against the same retry/restart issues). • Draft a small experiment plan and the logging you should capture to compare Krisp vs ai‑coustics in your Electron environment. • Review any ai‑coustics SDK docs you can share and point out integration risks (worklet lifecycle, native module conflicts, etc.) based on the Krisp experience. Which would you prefer — a code-level integration walkthrough, or a test plan + telemetry to compare both libraries? Sources: sip | livekit/livekit_composite | DeepWiki | Agents telephony integration | LiveKit Documentation, with interactive elements
c
Hi, unfortunately we haven't tried that yet. By the way, upgrading to the latest version of Krisp (0.4.1) seems to make the issue recur less frequently, we're currently testing this internally
t
Thanks — that’s helpful to know. Glad to hear upgrading to Krisp 0.4.1 is reducing the frequency of the failure during your tests. Quick suggestions to keep you moving while you continue testing: • Keep the defensive handling in place ◦ Add exponential backoff + a max retry cap when
createNoiseFilter
fails so you don’t get an infinite loop. ◦ Fully dispose of a failed processor before attempting a new one (stop ports/tracks and clear refs). • Harden reconnection logic ◦ Wrap
processor.restart()
in try/catch and ensure
sender.replaceTrack()
runs even when restart throws (either in finally or after catch recovery) so the mic can continue to send audio. • Add targeted telemetry ◦ Log Electron/Chromium versions, Krisp version (e.g., 0.4.1), timestamps, renderer restarts, and full error stacks for
WORKLET_NOT_SUPPORTED
/
DOMException
to help correlate failures with environment events. • Fallback UI/UX ◦ Surface a “Noise suppression disabled — retry” button after a few failed attempts so users can choose when to retry rather than having the client auto-loop. If you want, I can: • Show a small code patch that implements backoff + safe disposal and adds try/catch around
processor.restart()
. • Draft the exact telemetry fields and a short test plan to compare pre/ post-upgrade behavior. • Review any logs you collect to help identify environmental triggers. Which of those would be most useful right now? , with interactive elements