moeru-ai/airi · error · Error
Worklet loading failed
Error message
Worklet loading failed: ${err} What it means
Thrown by loadWorklets() inside initializeAudioContext (packages/audio) when context.audioWorklet.addModule() rejects for either the processor or the libsamplerate worklet module. The underlying error is interpolated into the message and workletLoaded stays false, so the context is unusable for AudioWorkletNode processing until a later initializeAudioContext call succeeds.
Solutions
- Read the wrapped error after 'Worklet loading failed:' — it names the exact addModule failure — and open the worklet URL directly (curl or browser tab) to check it returns 200 with a JavaScript MIME type
- If 404: make sure the worklet assets are copied to the build output and the URL is derived from import.meta.url / the correct public base
- If CSP-blocked: allow the worklet origin in worker-src and script-src (e.g. worker-src 'self' blob:)
- If the module throws on evaluation: import the worklet file in a plain script context to surface the syntax/import error
- Retry initializeAudioContext() after fixing — workletLoaded remains false so the load is reattempted
Example fix
// before
await initializeAudioContext(48000) // Error: Worklet loading failed: ...
// after
try {
await initializeAudioContext(48000)
}
catch (err) {
console.error('audio init failed, retrying once assets settle:', err)
await new Promise(r => setTimeout(r, 500))
await initializeAudioContext(48000) // workletLoaded stayed false, so loading is retried
} Defensive patterns
Strategy: try-catch
Validate before calling
// Verify the worklet asset URLs your build actually serves are reachable before init
for (const url of workletAssetUrls) {
const res = await fetch(url)
if (!res.ok || !(res.headers.get('content-type') ?? '').includes('javascript'))
console.warn(`worklet asset problem: ${url} -> ${res.status}`)
} Try / catch
try {
await initializeAudioContext(48000)
}
catch (err) {
// workletLoaded stays false, so a retry after fixing assets is safe
if (err instanceof Error && err.message.startsWith('Worklet loading failed')) {
reportAudioInitFailure(err) // inspect the wrapped cause, then retry or degrade gracefully
}
throw err
} Prevention
- Ensure worklet files are emitted/copied in both dev and prod builds and URLs derive from import.meta.url
- Add worker-src/script-src entries to CSP before shipping Electron/web apps
- Treat initializeAudioContext as the single gate: never create AudioWorkletNodes outside code that ran after it resolved
When it happens
Trigger: Calling initializeAudioContext() when ProcessorWorkletURL or LibsamplerateWorkletURL returns 404, is blocked by page CSP (worker-src/script-src), is served with a non-JavaScript MIME type, or when the worklet module itself throws during evaluation (syntax error, bad import).
Common situations: Bundler/dev-server does not emit the worklet files into build output; wrong base path so the URL resolves off-origin; strict Content-Security-Policy in Electron; CORS failure loading worklets from a CDN; worklet file references an identifier that does not exist in worklet scope.
Related errors
- AudioContext or worklets not ready
- AudioContext not initialized
- Camera or Renderer initialisation failure!
- Cannot declare permissions for unknown plugin
- Cannot grant permissions to unknown plugin
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/96cd9d205bfd43c4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/audio/src/audio-context/index.ts:73
catch (err) {
console.error('AudioContext state listener error:', err)
}
})
}
async function loadWorklets() {
if (!context || workletLoaded)
return
try {
await context.audioWorklet.addModule(ProcessorWorkletURL)
await context.audioWorklet.addModule(LibsamplerateWorkletURL)
workletLoaded = true
}
catch (err) {
console.error('Failed to load AudioWorklets:', err)
throw new Error(`Worklet loading failed: ${err}`)
}
}
export async function initializeAudioContext(requestedSampleRate: number = 48000): Promise<AudioContext> {
// Use high quality base sample rate
const baseSampleRate = Math.max(requestedSampleRate, 48000)
if (context && isReady && sampleRate === baseSampleRate && workletLoaded) {
return context
}
if (isInitializing) {
return new Promise((resolve, reject) => {
const checkReady = () => {
if (!isInitializing) {
if (context && isReady && workletLoaded) {
resolve(context)
}View on GitHub (pinned to 677329427f)