iflytek/astron-agent · error · Error

code (dynamic; from e.name||e.message, logged as '请求录音权限错误')

Error message

code (dynamic; from e.name||e.message, logged as '请求录音权限错误')

What it means

In recorder-core's getUserMedia permission request path (f2), any exception from navigator.mediaDevices.getUserMedia is converted to code = e.name || e.message || e.code+':'+e, logged as '请求录音权限错误', passed to codeFail ('无法录音:'+code), and thrown as new Error(code). Typical codes are NotAllowedError, NotFoundError, NotReadableError.

Solutions

  1. Map e.name to a user-facing message: NotAllowedError → enable permission, NotFoundError → connect a mic, NotReadableError → close apps using the mic
  2. Add allow="microphone" to iframes embedding the console
  3. Ensure HTTPS origin; getUserMedia is unavailable on insecure pages
  4. Catch the thrown code and offer a retry (requestPermission again) instead of crashing

Example fix

// before
rec.open(success, fail); // fail receives '无法录音:NotAllowedError'
// after
try {
  rec.open(success, (msg) => showMicError(msg));
} catch (code) {
  if (code === 'NotAllowedError') promptPermissionFix();
  else if (code === 'NotFoundError') promptConnectMic();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!window.isSecureContext || !navigator.mediaDevices) {
  message.error('无法录音:需要 HTTPS 且支持 getUserMedia');
  return;
}

Type guard

function hasMicAccess() { return !!navigator.mediaDevices?.enumerateDevices; }

Try / catch

try {
  rec.open(onOk, onFail);
} catch (code) {
  switch (code) {
    case 'NotAllowedError': showPermissionFix(); break;
    case 'NotFoundError': showNoMic(); break;
    case 'NotReadableError': showMicBusy(); break;
    default: console.error('请求录音权限错误:', code);
  }
}

Prevention

When it happens

Trigger: getUserMedia rejecting: user denied permission (NotAllowedError/SecurityError), no audio input device (NotFoundError), device busy (NotReadableError), or insecure context making mediaDevices undefined.

Common situations: Browser privacy settings blocking mic; external USB mic unplugged mid-session; kiosk browsers without media permissions policy; iframe missing allow="microphone".

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/e86c3576ca8abc8e. Report an issue: GitHub.

Appendix: source

Thrown at console/frontend/src/utils/record/recorder-core.js:875

          Recorder.Stream = stream;
          if (lockFail()) return;

          if (Recorder.IsOpen()) {
            if (oldStream) This.CLog('发现同时多次调用open', 1);

            Connect(streamStore, 1);
            ok();
          } else {
            failCall('录音功能无效:无音频流');
          }
        }, 100);
      };
      var f2 = function (e) {
        var code = e.name || e.message || e.code + ':' + e;
        This.CLog('请求录音权限错误', 1, e);

        codeFail(code, '无法录音:' + code);
        throw new Error(code);
      };

      var trackSet = {
        noiseSuppression: false, //默认禁用降噪,原声录制,免得移动端表现怪异(包括系统播放声音变小)
        echoCancellation: false, //回声消除
      };
      var trackSet2 = This.set.audioTrackSet;
      for (var k in trackSet2) trackSet[k] = trackSet2[k];
      trackSet.sampleRate = Recorder.Ctx.sampleRate; //必须指明采样率,不然手机上MediaRecorder采样率16k

      try {
        var pro = Recorder.Scope[getUserMediaTxt]({ audio: trackSet }, f1, f2);
      } catch (e) {
        //不能设置trackSet就算了
        This.CLog(getUserMediaTxt, 3, e);
        pro = Recorder.Scope[getUserMediaTxt]({ audio: true }, f1, f2);
      }
      if (pro && pro.then) {

View on GitHub (pinned to 5e758547a8)