Skip to main content

Overview

Robust error handling is essential for providing a good user experience. The React Voice Visualizer library provides error states and callbacks to help you handle various error scenarios that can occur during recording and playback.

The Error State

The hook returns an error state that contains any errors that occur:
import { useEffect } from "react";
import { useVoiceVisualizer, VoiceVisualizer } from "react-voice-visualizer";

function App() {
  const recorderControls = useVoiceVisualizer();
  const { error } = recorderControls;

  useEffect(() => {
    if (!error) return;

    // Handle the error
    console.error('Error:', error.message);
    alert(`An error occurred: ${error.message}`);
  }, [error]);

  return <VoiceVisualizer controls={recorderControls} />;
}
When an error occurs, the library automatically calls clearCanvas() to reset the visualizer state.

Common Error Scenarios

Microphone Permission Denied

The most common error occurs when users deny microphone access:
import { useEffect } from "react";
import { useVoiceVisualizer, VoiceVisualizer } from "react-voice-visualizer";

function RecorderWithPermissionHandling() {
  const recorderControls = useVoiceVisualizer();
  const { error, startRecording } = recorderControls;

  useEffect(() => {
    if (!error) return;

    // Check if it's a permission error
    if (error.message.includes('Permission') || 
        error.message.includes('denied') ||
        error.name === 'NotAllowedError') {
      // Show user-friendly message
      alert(
        'Microphone access is required to record audio. ' +
        'Please enable microphone permissions in your browser settings.'
      );
    }
  }, [error]);

  return (
    <div>
      <VoiceVisualizer controls={recorderControls} />
      {error && (
        <div style={{ color: 'red', marginTop: '10px' }}>
          Unable to access microphone. Please check your permissions.
        </div>
      )}
    </div>
  );
}
Browser microphone permissions are required for recording. Always provide clear instructions to users on how to enable permissions if they’re denied.

No Audio Device Available

This error occurs when the user’s device has no microphone:
useEffect(() => {
  if (!error) return;

  if (error.message.includes('Requested device not found') ||
      error.name === 'NotFoundError') {
    alert(
      'No microphone found. Please connect a microphone and try again.'
    );
  }
}, [error]);

Audio Processing Errors

Errors can occur when processing recorded audio:
useEffect(() => {
  if (!error) return;

  if (error.message.includes('processing the audio blob') ||
      error.message.includes('audio blob is empty')) {
    alert(
      'Failed to process the recording. Please try recording again.'
    );
  }
}, [error]);

Playback Error Callback

Use the onErrorPlayingAudio callback to handle errors during audio playback:
import { useVoiceVisualizer, VoiceVisualizer } from "react-voice-visualizer";

function App() {
  const recorderControls = useVoiceVisualizer({
    onErrorPlayingAudio: (error: Error) => {
      console.error('Playback error:', error);
      
      // Show user-friendly message
      if (error.message.includes('decode')) {
        alert('The audio file is corrupted and cannot be played.');
      } else {
        alert('Unable to play audio. Please try again.');
      }
    },
  });

  return <VoiceVisualizer controls={recorderControls} />;
}

Complete Error Handling Example

Here’s a comprehensive example with proper error handling:
import { useEffect, useState } from "react";
import { useVoiceVisualizer, VoiceVisualizer } from "react-voice-visualizer";

function RobustRecorder() {
  const [errorMessage, setErrorMessage] = useState<string | null>(null);

  const recorderControls = useVoiceVisualizer({
    onStartRecording: () => {
      setErrorMessage(null); // Clear previous errors
    },
    onErrorPlayingAudio: (error: Error) => {
      setErrorMessage(`Playback error: ${error.message}`);
    },
  });

  const { error, startRecording, clearCanvas } = recorderControls;

  useEffect(() => {
    if (!error) {
      setErrorMessage(null);
      return;
    }

    // Categorize and handle different error types
    let message: string;

    if (error.name === 'NotAllowedError' || error.message.includes('Permission')) {
      message = 'Microphone access denied. Please enable permissions and try again.';
    } else if (error.name === 'NotFoundError') {
      message = 'No microphone found. Please connect a microphone.';
    } else if (error.message.includes('audio blob is empty')) {
      message = 'Recording failed. The audio file is empty.';
    } else if (error.message.includes('processing')) {
      message = 'Failed to process the recording. Please try again.';
    } else {
      message = `An error occurred: ${error.message}`;
    }

    setErrorMessage(message);
  }, [error]);

  const handleStartRecording = () => {
    setErrorMessage(null);
    startRecording();
  };

  const handleClearError = () => {
    setErrorMessage(null);
    clearCanvas();
  };

  return (
    <div>
      <VoiceVisualizer controls={recorderControls} />

      {errorMessage && (
        <div style={{
          padding: '12px',
          backgroundColor: '#fef2f2',
          border: '1px solid #fecaca',
          borderRadius: '6px',
          color: '#991b1b',
          marginTop: '12px',
        }}>
          <strong>Error:</strong> {errorMessage}
          <button
            onClick={handleClearError}
            style={{ marginLeft: '12px' }}
          >
            Dismiss
          </button>
        </div>
      )}

      {!errorMessage && (
        <button onClick={handleStartRecording}>
          Start Recording
        </button>
      )}
    </div>
  );
}

Error Recovery Patterns

Automatic Retry

function RecorderWithRetry() {
  const [retryCount, setRetryCount] = useState(0);
  const MAX_RETRIES = 3;

  const recorderControls = useVoiceVisualizer();
  const { error, startRecording, clearCanvas } = recorderControls;

  useEffect(() => {
    if (!error || retryCount >= MAX_RETRIES) return;

    // Auto-retry after 2 seconds
    const timeout = setTimeout(() => {
      console.log(`Retrying... (${retryCount + 1}/${MAX_RETRIES})`);
      clearCanvas();
      startRecording();
      setRetryCount(prev => prev + 1);
    }, 2000);

    return () => clearTimeout(timeout);
  }, [error, retryCount]);

  return <VoiceVisualizer controls={recorderControls} />;
}

Fallback UI

function RecorderWithFallback() {
  const recorderControls = useVoiceVisualizer();
  const { error } = recorderControls;

  if (error && error.name === 'NotAllowedError') {
    return (
      <div>
        <h3>Microphone Access Required</h3>
        <p>This app needs microphone access to record audio.</p>
        <ol>
          <li>Click the lock icon in your browser's address bar</li>
          <li>Enable microphone permissions</li>
          <li>Refresh the page</li>
        </ol>
        <button onClick={() => window.location.reload()}>
          Refresh Page
        </button>
      </div>
    );
  }

  return <VoiceVisualizer controls={recorderControls} />;
}

Error Types Reference

Error NameDescriptionCommon Cause
NotAllowedErrorPermission deniedUser denied microphone access
NotFoundErrorDevice not foundNo microphone connected
NotReadableErrorHardware errorMicrophone in use by another app
OverconstrainedErrorConstraints not satisfiedRequested audio settings not supported
TypeErrorInvalid operationAttempting to use audio before it’s ready

Best Practices

1

Always Monitor the Error State

Use useEffect to watch for errors and respond appropriately:
useEffect(() => {
  if (!error) return;
  // Handle error
}, [error]);
2

Provide Clear Error Messages

Don’t show technical error messages to users. Translate them into actionable instructions:
// ❌ Bad
alert(error.message); // "NotAllowedError: Permission denied"

// ✅ Good
alert('Please enable microphone access in your browser settings.');
3

Show Recovery Options

Give users a way to recover from errors:
{error && (
  <div>
    <p>Something went wrong.</p>
    <button onClick={clearCanvas}>Try Again</button>
  </div>
)}
4

Log Errors for Debugging

Always log errors for troubleshooting:
useEffect(() => {
  if (!error) return;
  console.error('Recorder error:', {
    name: error.name,
    message: error.message,
    stack: error.stack,
  });
}, [error]);

User Feedback Components

Error Alert Component

function ErrorAlert({ error, onDismiss }) {
  if (!error) return null;

  return (
    <div role="alert" style={{
      padding: '16px',
      backgroundColor: '#fee2e2',
      border: '1px solid #fca5a5',
      borderRadius: '8px',
      color: '#7f1d1d',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'space-between',
    }}>
      <div>
        <strong>⚠️ Error:</strong> {error.message}
      </div>
      <button onClick={onDismiss} style={{
        background: 'none',
        border: 'none',
        cursor: 'pointer',
        fontSize: '20px',
      }}>
        ×
      </button>
    </div>
  );
}

// Usage
const { error, clearCanvas } = useVoiceVisualizer();

<ErrorAlert error={error} onDismiss={clearCanvas} />

Loading State for Error Recovery

function RecorderWithLoadingState() {
  const [isRetrying, setIsRetrying] = useState(false);
  const recorderControls = useVoiceVisualizer();
  const { error, clearCanvas, startRecording } = recorderControls;

  const handleRetry = async () => {
    setIsRetrying(true);
    clearCanvas();
    
    // Wait a bit before retrying
    await new Promise(resolve => setTimeout(resolve, 500));
    
    startRecording();
    setIsRetrying(false);
  };

  return (
    <div>
      <VoiceVisualizer controls={recorderControls} />
      {error && (
        <div>
          <p>Error: {error.message}</p>
          <button onClick={handleRetry} disabled={isRetrying}>
            {isRetrying ? 'Retrying...' : 'Try Again'}
          </button>
        </div>
      )}
    </div>
  );
}
Critical Scenarios: Always handle these error cases:
  • Microphone permission denied
  • No audio device available
  • Microphone in use by another application
  • Empty or corrupted audio blob

Testing Error Scenarios

To test error handling in development:
// Simulate permission denied
const recorderControls = useVoiceVisualizer({
  onStartRecording: () => {
    // Throw error for testing
    throw new Error('NotAllowedError: Permission denied');
  },
});

// Simulate empty blob
const recorderControls = useVoiceVisualizer({
  onStopRecording: () => {
    // Force error state
    throw new Error('Error: The audio blob is empty');
  },
});

Next Steps

Basic Usage

Learn the fundamentals

Hook API Reference

View all error-related callbacks

Build docs developers (and LLMs) love