block/buzz · error · Error

Cannot encode an empty voice note

Error message

Cannot encode an empty voice note

What it means

encodeVoiceNoteWav validates its inputs before building a WAV: samples must be non-empty and both input and output sample rates must be finite positive numbers. Any violation — including non-finite or non-positive rates — is reported uniformly as "Cannot encode an empty voice note" so the encoder never produces a degenerate file.

Source

Thrown at desktop/src/features/messages/lib/voiceNoteWav.ts:23

    view.setUint8(offset + index, value.charCodeAt(index));
  }
}

export function encodeVoiceNoteWav(
  channels: readonly Float32Array[],
  inputSampleRate: number,
  outputSampleRate = DEFAULT_OUTPUT_SAMPLE_RATE,
): Uint8Array {
  const inputLength = channels[0]?.length ?? 0;
  if (
    channels.length === 0 ||
    inputLength === 0 ||
    !Number.isFinite(inputSampleRate) ||
    inputSampleRate <= 0 ||
    !Number.isFinite(outputSampleRate) ||
    outputSampleRate <= 0
  ) {
    throw new Error("Cannot encode an empty voice note");
  }

  const frameCount = Math.max(
    1,
    Math.floor((inputLength * outputSampleRate) / inputSampleRate),
  );
  const bytes = new Uint8Array(44 + frameCount * 2);
  const view = new DataView(bytes.buffer);
  writeAscii(view, 0, "RIFF");
  view.setUint32(4, bytes.length - 8, true);
  writeAscii(view, 8, "WAVE");
  writeAscii(view, 12, "fmt ");
  view.setUint32(16, 16, true);
  view.setUint16(20, 1, true);
  view.setUint16(22, 1, true);
  view.setUint32(24, outputSampleRate, true);
  view.setUint32(28, outputSampleRate * 2, true);
  view.setUint16(32, 2, true);

View on GitHub (pinned to dad5a33865)

Solutions

  1. Check samples.length (and the recording duration) before calling encodeVoiceNoteWav and skip encode for empty captures.
  2. Verify the AudioContext sampleRate is a finite positive number before recording starts; recreate the context if it reports NaN/0.
  3. Confirm microphone permissions are granted so capture actually produces samples.
  4. Fix the resampler configuration so outputSampleRate is a valid positive constant (e.g. 16000).

Example fix

// before
const wav = encodeVoiceNoteWav(samples, ctx.sampleRate, 16000);

// after
if (samples.length === 0 || !Number.isFinite(ctx.sampleRate) || ctx.sampleRate <= 0) {
  toast.error("Recording was empty — try again.");
  return;
}
const wav = encodeVoiceNoteWav(samples, ctx.sampleRate, 16000);
Defensive patterns

Strategy: validation

Validate before calling

const ok = samples.length > 0
  && Number.isFinite(inputSampleRate) && inputSampleRate > 0
  && Number.isFinite(outputSampleRate) && outputSampleRate > 0;
if (!ok) skipEncodeAndNotifyUser();

Type guard

function isEncodableAudio(samples: Float32Array, inRate: number, outRate: number): boolean {
  return samples.length > 0
    && Number.isFinite(inRate) && inRate > 0
    && Number.isFinite(outRate) && outRate > 0;
}

Try / catch

try {
  const wav = encodeVoiceNoteWav(samples, inRate, outRate);
} catch (e) {
  if (e instanceof Error && e.message === "Cannot encode an empty voice note") {
    toast.error("Recording was empty — hold to record again.");
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Recording produced zero samples (inputLength === 0), or a Float32Array of length 0 was passed, or sample rates are NaN/Infinity/<= 0 (e.g. an AudioContext with a closed/uninitialized sampleRate).

Common situations: User taps record and immediately stops (empty capture); microphone permission denied leading to an empty buffer; getFloatTimeDomainData on a torn-down AudioContext returning NaN sampleRate; resampler config bug passing 0 as output rate.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-09-05). Data as JSON: /api/errors/fe6ad315df54a1ec. Report an issue: GitHub.