jackwener/OpenCLI · error · CommandExecutionError
MiniMax music returned bytes that are not a WAV file
Error message
MiniMax music returned bytes that are not a WAV file
What it means
decodeAudioHex decodes the hex-encoded audio payload MiniMax returns for music generation and verifies it matches the requested container format. When audioFormat is 'wav', the library throws this CommandExecutionError if the decoded bytes lack a valid RIFF/WAVE header (must start with 'RIFF' and contain 'WAVE' at offset 8, minimum 12 bytes). This guards against the MiniMax API silently returning wrong-format or corrupted audio.
Source
Thrown at clis/minimax/utils.js:157
} catch {
throw new CommandExecutionError('MiniMax music returned data.audio that is not a URL');
}
if (url.protocol !== 'https:') {
throw new CommandExecutionError(`MiniMax music returned a non-HTTPS audio URL (${url.protocol})`);
}
return url.toString();
}
export function decodeAudioHex(value, expectedBytes, format) {
if (value.length === 0 || value.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(value)) {
throw new CommandExecutionError('MiniMax music returned invalid hexadecimal audio');
}
const bytes = Buffer.from(value, 'hex');
if (expectedBytes != null && bytes.length !== expectedBytes) {
throw new CommandExecutionError(`MiniMax music audio size mismatch (expected ${expectedBytes} bytes, got ${bytes.length})`);
}
if (format === 'wav' && (bytes.length < 12 || bytes.subarray(0, 4).toString('ascii') !== 'RIFF' || bytes.subarray(8, 12).toString('ascii') !== 'WAVE')) {
throw new CommandExecutionError('MiniMax music returned bytes that are not a WAV file');
}
if (format === 'mp3' && !isMp3(bytes)) {
throw new CommandExecutionError('MiniMax music returned bytes that are not an MP3 file');
}
return bytes;
}
function isMp3(bytes) {
return bytes.length >= 3 && bytes.subarray(0, 3).toString('ascii') === 'ID3'
|| bytes.length >= 2 && bytes[0] === 0xff && (bytes[1] & 0xe0) === 0xe0;
}
export function resolveOutputDir(value) {
const raw = String(value ?? '').trim();
if (!raw) return path.join(os.homedir(), 'Music', 'minimax');
if (raw === '~') return os.homedir();
if (raw.startsWith('~/')) return path.join(os.homedir(), raw.slice(2));
if (raw.startsWith('~')) throw new ArgumentError(`Unsupported home-directory path: ${raw}`);View on GitHub (pinned to 49907e53dc)
Solutions
- Verify the request body sent audio_setting.format='wav' and re-run the generation
- Decode a few bytes of data.audio and check whether they actually look like MP3 (ID3 or 0xFFEx sync) — if so, request format='mp3' instead
- Retry the music_generation call; if it reproduces, capture the response (trace_id, base_resp) and report to MiniMax support
- Check the hex string for corruption upstream (odd length, non-hex chars would already fail earlier; here suspect truncation before decode)
Example fix
// before: requesting WAV but API returned MP3 bytes
const bytes = decodeAudioHex(data.audio, expectedBytes, 'wav'); // throws
// after: request the format the API actually honors
const bytes = decodeAudioHex(data.audio, expectedBytes, 'mp3');
fs.writeFileSync('out.mp3', bytes); Defensive patterns
Strategy: validation
Validate before calling
function looksLikeWav(hex) {
if (!/^[0-9a-f]+$/i.test(hex) || hex.length % 2 !== 0) return false;
const bytes = Buffer.from(hex, 'hex');
return bytes.length >= 12
&& bytes.subarray(0, 4).toString('ascii') === 'RIFF'
&& bytes.subarray(8, 12).toString('ascii') === 'WAVE';
}
// call before decodeAudioHex(..., 'wav'): if (!looksLikeWav(data.audio)) request mp3 or retry Type guard
function isWavBuffer(bytes) {
return Buffer.isBuffer(bytes) && bytes.length >= 12
&& bytes.subarray(0, 4).toString('ascii') === 'RIFF'
&& bytes.subarray(8, 12).toString('ascii') === 'WAVE';
} Try / catch
try {
const bytes = decodeAudioHex(data.audio, expectedBytes, 'wav');
} catch (e) {
if (String(e.message).includes('not a WAV file')) {
// fall back: detect actual format and save accordingly
const mp3 = decodeAudioHex(data.audio, expectedBytes, 'mp3');
fs.writeFileSync(out + '.mp3', mp3);
} else throw e;
} Prevention
- Always send audio_setting.format explicitly and consistently in buildRequest
- Sniff the first bytes of decoded audio before saving and derive the file extension from them
- Log base_resp/trace_id when format mismatches occur to support MiniMax reports
- Pin/monitor API behavior changes; add an integration test asserting WAV magic bytes
When it happens
Trigger: Calling decodeAudioHex (via the MiniMax music flow) with format='wav' while the decoded hex payload does not begin with 'RIFF'....'WAVE' — e.g. the API returned MP3 bytes despite a WAV request, or a truncated payload shorter than 12 bytes.
Common situations: MiniMax changes or misconfigures its audio_setting.format handling server-side; a proxy or cached response substitutes a different codec; the hex string was truncated or mangled in transit so the header check fails.
Related errors
- MiniMax music returned bytes that are not an MP3 file
- MiniMax music returned data.audio that is not a URL
- MiniMax music returned invalid hexadecimal audio
- ${raw} is not a Douyin sec_uid
- designer must be a Dribbble username or profile slug
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/50b0f28303874bba.
Report an issue: GitHub.