iflytek/astron-agent · error · Error
录音器未初始化
Error message
录音器未初始化
What it means
The recorder component obtains an RTASR (real-time speech) token, then verifies that the lazily-created recorder instance stored in `record.current` exists before calling `recStart`. If the ref is still null at start time, it throws '录音器未初始化' (recorder not initialized). This is an internal lifecycle guard: the recorder object is constructed asynchronously (often after user permission is granted), so recording can start before initialization completes.
Solutions
- Initialize the recorder instance before requesting the token: construct RecordApp/the recorder first, await its init, then call getRtasrToken() and recStart.
- Disable the record button until the recorder reports ready (state flag set after async init resolves).
- Check `navigator.mediaDevices.getUserMedia`/permission first so the user grants mic access before clicking record.
- Guard against double-click: ignore or debounce the start handler while a start is in flight.
Example fix
// before
const tokenResponse = await getRtasrToken();
if (!record.current) {
throw new Error('录音器未初始化');
}
await record.current.recStart(tokenResponse);
// after
// ensure recorder is created before requesting token
if (!record.current) {
record.current = await createRecorder(); // awaited init
}
const tokenResponse = await getRtasrToken();
await record.current.recStart(tokenResponse); Defensive patterns
Strategy: try-catch
Validate before calling
if (typeof RecordApp === 'undefined' || !record.current) {
setStatus('idle');
return; // don't attempt recStart
} Type guard
function isRecorderReady(r: unknown): r is { recStart: (t: unknown) => Promise<void> } {
return !!r && typeof (r as any)?.recStart === 'function';
} Try / catch
try {
await startRecording();
} catch (e) {
if (e.message === '录音器未初始化') {
setStatus('idle');
message.warning('录音组件尚未就绪,请稍后重试');
} else { throw e; }
} Prevention
- Await recorder async init before enabling the record button.
- Track a `recorderReady` state flag set after initialization resolves.
- Debounce the start handler to prevent double-click races.
- Handle microphone permission request before constructing the token flow.
When it happens
Trigger: Clicking the record button (which calls getRtasrToken() then record.current.recStart()) before the recorder instance has finished asynchronous construction/assignment into the `record` ref — e.g. microphone permission prompt still pending, recorder script not yet loaded, or rapid double-click starting twice.
Common situations: Slow network delaying the recorder library load, first-ever mic permission grant (user clicks record before the permission callback resolves), React StrictMode double-effect causing the ref to be cleared, or a race between unmount cleanup nulling the ref and the in-flight async start.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/3c99f0acd7ae533a.
Report an issue: GitHub.
Appendix: source
Thrown at console/frontend/src/pages/chat-page/components/recorder-com.tsx:72
timer = null;
}
setStatus('end');
} catch (error) {
console.warn('停止录音失败:', error);
}
}, [changeStatus]);
// 开始录音事件处理
const handleStartRecord = useCallback(async (): Promise<void> => {
if (disabled || (status !== 'ready' && status !== 'end')) {
return;
}
try {
const tokenResponse = await getRtasrToken();
if (!record.current) {
throw new Error('录音器未初始化');
}
await record.current.recStart(tokenResponse);
setStatus('start');
changeStatus && changeStatus('play');
// 设置60秒超时
timer = setTimeout(() => {
stopAudio();
changeStatus && changeStatus('end');
}, 60 * 1000);
} catch (error) {
console.warn('录音启动失败:', error);
// 类型安全的错误处理
if (error && typeof error === 'object' && 'detail' in error) {
const errorDetail = error.detail as { code?: number };
if (errorDetail.code && [80000, 90000].includes(errorDetail.code)) {
return;View on GitHub (pinned to 5e758547a8)