heygen-com/hyperframes · error · AudioFxRenderError
Not a WAV file: ${path}
Error message
Not a WAV file: ${path} What it means
readWav() reads a file synchronously and rejects it as `Not a WAV file` if it is shorter than 44 bytes (too small to contain even the canonical header) or its first four bytes are not the ASCII `RIFF` magic. This is the minimal sanity gate before walking WAV chunks; only files the engine's own mixer/extract pipeline emits are supported.
Source
Thrown at packages/engine/src/services/audioFxRender.ts:72
const size = buf.readUInt32LE(offset + 4);
if (id === "fmt ") {
head.format = buf.readUInt16LE(offset + 8);
head.channels = buf.readUInt16LE(offset + 10);
head.sampleRate = buf.readUInt32LE(offset + 12);
head.bits = buf.readUInt16LE(offset + 22);
} else if (id === "data") {
data = buf.subarray(offset + 8, Math.min(buf.length, offset + 8 + size));
break;
}
offset += 8 + size + (size % 2);
}
return { ...head, data };
}
export function readWav(path: string): WavData {
const buf = readFileSync(path);
if (buf.length < 44 || buf.toString("ascii", 0, 4) !== "RIFF") {
throw new AudioFxRenderError(`Not a WAV file: ${path}`);
}
const { format, channels, sampleRate, bits, data } = readWavChunks(buf);
if (!data) throw new AudioFxRenderError(`WAV has no data chunk: ${path}`);
return { samples: decodeSamples(data, format, bits, path), sampleRate, channels };
}
/** Interleaved samples as floats, for the two formats the mixer emits upstream. */
function decodeSamples(data: Buffer, format: number, bits: number, path: string): Float32Array {
if (format === 3 && bits === 32) {
const n = Math.floor(data.length / 4);
// A Float32Array view demands a 4-aligned offset, and chunk layouts that put
// `data` on an odd boundary (an 18-byte fmt plus a fact chunk, which
// ffmpeg's pcm_f32le writes) would otherwise throw RangeError. Copy then.
if (data.byteOffset % 4 === 0) return new Float32Array(data.buffer, data.byteOffset, n);
const copied = new Float32Array(n);
for (let i = 0; i < n; i++) copied[i] = data.readFloatLE(i * 4);
return copied;
}View on GitHub (pinned to c2996c8626)
Solutions
- Confirm the file is actually WAV PCM/float: `file <path>` or inspect the first bytes (`xxd <path> | head -1`) — expect `RIFF`.
- If it is a different format, convert upstream: `ffmpeg -i in.mp3 -c:a pcm_s16le out.wav` (16-bit PCM) or `pcm_f32le` for float.
- If the file is empty/truncated, re-run the upstream render/mix step that produced it.
- Ensure readWav is only called on files produced by the engine's own mixer/extract pipeline.
Example fix
# before: source is mp3 mislabeled $ file track.wav # -> MPEG ADTS # after: transcode to the WAV the mixer emits $ ffmpeg -i track.wav -c:a pcm_s16le -ar 48000 -ac 2 track.pcm.wav
Defensive patterns
Strategy: type-guard
Validate before calling
import { readFileSync, statSync } from "node:fs";
function isWavFile(path: string): boolean {
let buf;
try { buf = readFileSync(path); } catch { return false; }
return buf.length >= 44 && buf.toString("ascii", 0, 4) === "RIFF";
}
if (!isWavFile(path)) throw new Error(`not a WAV file: ${path}`); Type guard
import { readFileSync } from "node:fs";
function isPcmWav(path: string): boolean {
try {
const b = readFileSync(path, { start: 0, end: 44 });
return b.toString("ascii", 0, 4) === "RIFF" && b.toString("ascii", 8, 12) === "WAVE";
} catch { return false; }
} Try / catch
import { AudioFxRenderError } from "@hyperframes/engine/services/audioFxRender";
try {
return readWav(path);
} catch (e) {
if (e instanceof AudioFxRenderError && /Not a WAV file/.test(e.message)) {
throw new Error(`upstream produced a non-WAV audio asset at ${path}; check the mixer/extract step`, { cause: e });
}
throw e;
} Prevention
- Only feed readWav files produced by the engine's mixer/extract pipeline (16-bit PCM or 32-bit float WAV).
- Run `file <path>` and confirm `RIFF` before invoking.
- Convert foreign audio with `ffmpeg -i in -c:a pcm_s16le out.wav` upstream.
When it happens
Trigger: Calling readWav(path) on a non-WAV file (MP3, AAC, FLAC, OGG), a truncated/empty file, a RIFX (big-endian) WAV, or a file written with a non-RIFF container. The audio FX render step hands it the WAV produced upstream by the mixer; pointing it at anything else triggers this.
Common situations: An upstream audio step wrote MP3/AAC instead of WAV (codec mismatch); a render was interrupted leaving a 0-byte WAV; a user dropped an .mp3 renamed to .wav; the source asset is big-endian RIFX from an uncommon encoder.
Related errors
- WAV has no data chunk: ${path}
- Unsupported WAV format ${format}/${bits}-bit: ${path}
- Unsupported input: ${ext}. Use a video (mp4/mov/webm/mkv/avi
- Audio file too large for headless analysis (${mb}MB > ${MAX_
- ffmpeg is required to extract audio from video. Install: ${g
AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12).
Data as JSON: /api/errors/145f9e8753969b8a.
Report an issue: GitHub.