{"record":{"id":"fe6ad315df54a1ec","repo":"block/buzz","slug":"cannot-encode-an-empty-voice-note","errorCode":null,"errorMessage":"Cannot encode an empty voice note","messagePattern":"Cannot encode an empty voice note","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"desktop/src/features/messages/lib/voiceNoteWav.ts","lineNumber":23,"sourceCode":"    view.setUint8(offset + index, value.charCodeAt(index));\n  }\n}\n\nexport function encodeVoiceNoteWav(\n  channels: readonly Float32Array[],\n  inputSampleRate: number,\n  outputSampleRate = DEFAULT_OUTPUT_SAMPLE_RATE,\n): Uint8Array {\n  const inputLength = channels[0]?.length ?? 0;\n  if (\n    channels.length === 0 ||\n    inputLength === 0 ||\n    !Number.isFinite(inputSampleRate) ||\n    inputSampleRate <= 0 ||\n    !Number.isFinite(outputSampleRate) ||\n    outputSampleRate <= 0\n  ) {\n    throw new Error(\"Cannot encode an empty voice note\");\n  }\n\n  const frameCount = Math.max(\n    1,\n    Math.floor((inputLength * outputSampleRate) / inputSampleRate),\n  );\n  const bytes = new Uint8Array(44 + frameCount * 2);\n  const view = new DataView(bytes.buffer);\n  writeAscii(view, 0, \"RIFF\");\n  view.setUint32(4, bytes.length - 8, true);\n  writeAscii(view, 8, \"WAVE\");\n  writeAscii(view, 12, \"fmt \");\n  view.setUint32(16, 16, true);\n  view.setUint16(20, 1, true);\n  view.setUint16(22, 1, true);\n  view.setUint32(24, outputSampleRate, true);\n  view.setUint32(28, outputSampleRate * 2, true);\n  view.setUint16(32, 2, true);","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/desktop/src/features/messages/lib/voiceNoteWav.ts#L5-L41","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check samples.length (and the recording duration) before calling encodeVoiceNoteWav and skip encode for empty captures.","Verify the AudioContext sampleRate is a finite positive number before recording starts; recreate the context if it reports NaN/0.","Confirm microphone permissions are granted so capture actually produces samples.","Fix the resampler configuration so outputSampleRate is a valid positive constant (e.g. 16000)."],"exampleFix":"// before\nconst wav = encodeVoiceNoteWav(samples, ctx.sampleRate, 16000);\n\n// after\nif (samples.length === 0 || !Number.isFinite(ctx.sampleRate) || ctx.sampleRate <= 0) {\n  toast.error(\"Recording was empty — try again.\");\n  return;\n}\nconst wav = encodeVoiceNoteWav(samples, ctx.sampleRate, 16000);","handlingStrategy":"validation","validationCode":"const ok = samples.length > 0\n  && Number.isFinite(inputSampleRate) && inputSampleRate > 0\n  && Number.isFinite(outputSampleRate) && outputSampleRate > 0;\nif (!ok) skipEncodeAndNotifyUser();","typeGuard":"function isEncodableAudio(samples: Float32Array, inRate: number, outRate: number): boolean {\n  return samples.length > 0\n    && Number.isFinite(inRate) && inRate > 0\n    && Number.isFinite(outRate) && outRate > 0;\n}","tryCatchPattern":"try {\n  const wav = encodeVoiceNoteWav(samples, inRate, outRate);\n} catch (e) {\n  if (e instanceof Error && e.message === \"Cannot encode an empty voice note\") {\n    toast.error(\"Recording was empty — hold to record again.\");\n    return;\n  }\n  throw e;\n}","preventionTips":["Check captured buffer length and duration before encode; discard empty takes.","Validate AudioContext.sampleRate is finite and positive when creating the recorder.","Assert microphone permissions before starting capture.","Pin output sample rate to a positive constant and unit-test encode with zero-length input."],"tags":["audio","validation","voice-note","wav"],"backgroundTag":"empty-audio-input","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-09-05T18:13:50.666Z","contentChangedAt":"2026-09-05T18:13:50.666Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}