{"record":{"id":"30c3fdeb0941345a","repo":"iflytek/astron-agent","slug":"buffers","errorCode":null,"errorMessage":"音频buffers被释放","messagePattern":"音频buffers被释放","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"console/frontend/src/utils/record/recorder-core.js","lineNumber":1387,"sourceCode":"      };\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 + '编码器');\n        return;\n      }\n\n      //环境配置检查，此处仅针对mock调用，因为open已经检查过了\n      if (This.isMock) {\n        var checkMsg = This.envCheck(\n          This.mockEnvInfo || { envName: 'mock', canProcess: false }\n        ); //没有提供环境信息的mock时没有onProcess回调\n        if (checkMsg) {\n          err('录音错误：' + checkMsg);\n          return;\n        }\n      }\n","sourceCodeStart":1369,"sourceCodeEnd":1405,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/console/frontend/src/utils/record/recorder-core.js#L1369-L1405","documentation":"Recorder-Core's stop() found recSize > 0 but This.buffers[0] is missing, meaning the accumulated PCM buffer store was cleared (or never held) — typically because buffers were released after a previous stop or clear, or recording state and buffer store got out of sync.","triggerScenarios":"rec.stop() invoked when recSize is nonzero but This.buffers[0] is undefined/falsy, i.e. the buffer array was emptied (e.g. rec.close() or buffer release happened) while recSize was not reset, or a second stop() raced the first.","commonSituations":"Double-invoking stop(); calling stop() after close() with stale size counters; using takeoffEncodeChunk where buffers are consumed/streamed and then released; manual buffer manipulation by application code.","solutions":["Don't call stop() twice; disable the stop button after the first invocation.","Only call stop() while the recorder is in a recording state (check rec.state).","Avoid manually mutating rec.buffers; rely on library APIs (start/stop/close).","After close(), recreate or re-open the recorder instead of stopping again.","If streaming with takeoffEncodeChunk, don't also expect stop() to return a full blob."],"exampleFix":"// before\nrec.stop();\nrec.stop(); // second call: buffers released\n\n// after\nvar stopped = false;\nstopBtn.onclick = function() {\n  if (stopped || rec.state !== 1) return;\n  stopped = true;\n  rec.stop(function(blob) { upload(blob); });\n};","handlingStrategy":"validation","validationCode":"function canStopSafely(rec) {\n  return rec && rec.state === 1 && rec.recSize > 0 && rec.buffers && rec.buffers[0];\n}\nif (!canStopSafely(rec)) return;","typeGuard":"function buffersAvailable(rec) { return Array.isArray(rec.buffers) && rec.buffers.length > 0 && !!rec.buffers[0]; }","tryCatchPattern":"try { rec.stop(cb); } catch (e) {\n  if (String(e).includes('音频buffers被释放')) resetRecorder();\n}","preventionTips":["Guard against double stop() invocations with a flag or disabled button.","Never call stop() after close(); recreate the recorder instead.","Don't mutate rec.buffers manually.","Reset UI state (recording flag) immediately after the first stop."],"tags":["audio","recording","state-management","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"}