{"record":{"id":"43625beefb79db82","repo":"iflytek/astron-agent","slug":"isctxwait-audiocontext","errorCode":null,"errorMessage":"未开始录音${isCtxWait ? '，开始录音前无用户交互导致AudioContext未运行' : ''}","messagePattern":"未开始录音\\$\\{isCtxWait \\? '，开始录音前无用户交互导致AudioContext未运行' : ''\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"console/frontend/src/utils/record/recorder-core.js","lineNumber":1373,"sourceCode":"        if (set.takeoffEncodeChunk) {\n          //接管了输出，此时blob长度为0\n          This.CLog(\n            '启用takeoffEncodeChunk后stop返回的blob长度为0不提供音频数据',\n            3\n          );\n        } else if (blob.size < Math.max(100, duration / 2)) {\n          //1秒小于0.5k？\n          err('生成的' + set.type + '无效');\n          return;\n        }\n        True &&\n          True(blob, duration, originBUffers || [], originSampleRate || 44100);\n        end();\n      };\n      if (!This.isMock) {\n        var isCtxWait = This.state == 3;\n        if (!This.state || isCtxWait) {\n          err(\n            '未开始录音' +\n              (isCtxWait ? '，开始录音前无用户交互导致AudioContext未运行' : '')\n          );\n          return;\n        }\n        This._stop(true);\n      }\n      var size = This.recSize;\n      if (!size) {\n        err('未采集到录音');\n        return;\n      }\n      if (!This.buffers[0]) {\n        err('音频buffers被释放');\n        return;\n      }\n      if (!This[set.type]) {\n        err('未加载' + set.type + '编码器');","sourceCodeStart":1355,"sourceCodeEnd":1391,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/frontend/src/utils/record/recorder-core.js#L1355-L1391","documentation":"Recorder-Core's stop() fails because recording never actually started: This.state is 0 (never opened/started) or 3 (open succeeded but the AudioContext is suspended because the browser requires a user gesture before audio processing). The isCtxWait suffix clarifies that user interaction was missing, so the AudioContext never left the 'suspended' state.","triggerScenarios":"Calling Recorder.stop() when This.state is falsy (recorder never opened) or state==3; state 3 arises when rec.open() succeeded but rec.start() was deferred waiting for a user gesture and none occurred, leaving the browser AudioContext suspended.","commonSituations":"Calling stop() without a prior start(); attempting to auto-start recording on page load without a click/tap (Chrome/Safari autoplay policy); an async permission prompt consumed the gesture window; SPA navigated away before start was triggered.","solutions":["Ensure rec.start() is called inside a direct user-gesture handler (click/touchend) so the AudioContext resumes.","Guard stop() calls: only invoke when recording state confirms start succeeded.","If you defer start, prompt the user to interact (e.g. 'Tap to start recording') and call start() in that handler.","Check mic permission was granted; a pending/denied permission can leave state unresolved.","After resume() on the AudioContext, verify rec.state before stopping."],"exampleFix":"// before\nrec.open(function() { /* waiting... */ });\nrec.stop(); // state==3 -> error\n\n// after\nrec.open(function() {\n  startBtn.onclick = function() {\n    rec.start();\n  };\n});\nstopBtn.onclick = function() {\n  if (rec.state === 1) rec.stop();\n};","handlingStrategy":"validation","validationCode":"function canStop(rec) {\n  return rec && typeof rec.state === 'number' && rec.state === 1;\n}\nif (!canStop(rec)) return; // don't call stop unless recording","typeGuard":"function isRecording(rec) { return !!rec && rec.state === 1; }","tryCatchPattern":"try { rec.stop(onDone); } catch (e) {\n  if (String(e).includes('未开始录音')) {\n    showToast('Please start recording first (tap to allow audio)');\n  }\n}","preventionTips":["Always call start() inside a user-gesture handler.","Check rec.state before calling stop().","Never attempt to auto-start recording on page load without interaction.","Handle permission prompts asynchronously and start after grant."],"tags":["audio","recording","browser","autoplay-policy","javascript"],"backgroundTag":"invalid-state-transition","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}