{"record":{"id":"88130c32fef316a5","repo":"mudler/LocalAI","slug":"web-audio-api-is-not-available-in-this-browser","errorCode":null,"errorMessage":"Web Audio API is not available in this browser","messagePattern":"Web Audio API is not available in this browser","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/http/react-ui/src/pages/VoiceProfileCreate.jsx","lineNumber":27,"sourceCode":"import { audioBufferToWavBlob } from '../hooks/useMediaCapture'\nimport { voiceProfilesApi } from '../utils/api'\n\nconst MAX_AUDIO_BYTES = 50 * 1024 * 1024\nconst REFERENCE_SAMPLE_RATE = 24000\n\nfunction base64ToArrayBuffer(value) {\n  const binary = window.atob(value)\n  const bytes = new Uint8Array(binary.length)\n  for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index)\n  return bytes.buffer\n}\n\nasync function normalizeAudioSample(sample) {\n  const source = sample.blob?.arrayBuffer\n    ? await sample.blob.arrayBuffer()\n    : base64ToArrayBuffer(sample.base64)\n  const AudioCtx = window.AudioContext || window.webkitAudioContext\n  if (!AudioCtx) throw new Error('Web Audio API is not available in this browser')\n  const context = new AudioCtx()\n  try {\n    const decoded = await context.decodeAudioData(source.slice(0))\n    const blob = audioBufferToWavBlob(decoded, REFERENCE_SAMPLE_RATE)\n    if (blob.size > MAX_AUDIO_BYTES) throw new Error('Normalized audio is larger than 50 MiB')\n    return {\n      ...sample,\n      blob,\n      dataUrl: URL.createObjectURL(blob),\n      objectUrl: true,\n      mime: 'audio/wav',\n      duration: decoded.duration,\n      sampleRate: REFERENCE_SAMPLE_RATE,\n      name: sample.name || 'recording.wav',\n    }\n  } finally {\n    await context.close().catch(() => {})\n  }","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/core/http/react-ui/src/pages/VoiceProfileCreate.jsx#L9-L45","documentation":"Thrown by normalizeAudioSample() when neither window.AudioContext nor window.webkitAudioContext exists, so the recorded/uploaded voice sample cannot be decoded and resampled. The function needs decodeAudioData to convert the source (Blob via arrayBuffer, or base64 via atob) into a WAV blob at REFERENCE_SAMPLE_RATE before upload.","triggerScenarios":"Running the voice-profile creation flow in a browser with Web Audio disabled or unavailable: very old browsers, embedded webviews, browsers with strict audio-protection flags, or non-secure contexts where AudioContext is restricted.","commonSituations":"Opening the UI in a legacy/embedded webview (Electron without audio support, old Android WebView); accessing the UI over plain HTTP from a browser that gates AudioContext behind secure contexts; enterprise policy disabling Web Audio.","solutions":["Use a modern Chromium/Firefox/Safari release that ships Web Audio","Serve the UI over HTTPS or localhost so secure-context restrictions do not apply","If in an embedded webview, enable its media/audio runtime flags","As a fallback, pre-convert the sample to 16 kHz mono WAV externally and upload where normalization is bypassed (if the flow allows)"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Feature-detect before entering the voice-profile flow\nexport function webAudioAvailable() {\n  return Boolean(window.AudioContext || window.webkitAudioContext)\n}","typeGuard":"function supportsWebAudio() {\n  return typeof window !== 'undefined' && Boolean(window.AudioContext || window.webkitAudioContext)\n}","tryCatchPattern":"try {\n  const normalized = await normalizeAudioSample(sample)\n} catch (err) {\n  if (err.message.includes('Web Audio API')) {\n    setUnsupportedBanner('Voice profiles require a browser with Web Audio support')\n    return // disable the flow instead of showing a raw error\n  }\n  throw err\n}","preventionTips":["Gate the voice-profile UI on supportsWebAudio() and show a requirements notice instead of an error","Serve over HTTPS/localhost so secure-context restrictions do not hide AudioContext","Test in the oldest webview you support before shipping the feature"],"tags":["web-audio","browser-support","voice","react-ui"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}