{"record":{"id":"668c46015f71576f","repo":"jackwener/OpenCLI","slug":"minimax-music-returned-bytes-that-are-not-an-mp3-f","errorCode":null,"errorMessage":"MiniMax music returned bytes that are not an MP3 file","messagePattern":"MiniMax music returned bytes that are not an MP3 file","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/minimax/utils.js","lineNumber":160,"sourceCode":"    if (url.protocol !== 'https:') {\n        throw new CommandExecutionError(`MiniMax music returned a non-HTTPS audio URL (${url.protocol})`);\n    }\n    return url.toString();\n}\n\nexport function decodeAudioHex(value, expectedBytes, format) {\n    if (value.length === 0 || value.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(value)) {\n        throw new CommandExecutionError('MiniMax music returned invalid hexadecimal audio');\n    }\n    const bytes = Buffer.from(value, 'hex');\n    if (expectedBytes != null && bytes.length !== expectedBytes) {\n        throw new CommandExecutionError(`MiniMax music audio size mismatch (expected ${expectedBytes} bytes, got ${bytes.length})`);\n    }\n    if (format === 'wav' && (bytes.length < 12 || bytes.subarray(0, 4).toString('ascii') !== 'RIFF' || bytes.subarray(8, 12).toString('ascii') !== 'WAVE')) {\n        throw new CommandExecutionError('MiniMax music returned bytes that are not a WAV file');\n    }\n    if (format === 'mp3' && !isMp3(bytes)) {\n        throw new CommandExecutionError('MiniMax music returned bytes that are not an MP3 file');\n    }\n    return bytes;\n}\n\nfunction isMp3(bytes) {\n    return bytes.length >= 3 && bytes.subarray(0, 3).toString('ascii') === 'ID3'\n        || bytes.length >= 2 && bytes[0] === 0xff && (bytes[1] & 0xe0) === 0xe0;\n}\n\nexport function resolveOutputDir(value) {\n    const raw = String(value ?? '').trim();\n    if (!raw) return path.join(os.homedir(), 'Music', 'minimax');\n    if (raw === '~') return os.homedir();\n    if (raw.startsWith('~/')) return path.join(os.homedir(), raw.slice(2));\n    if (raw.startsWith('~')) throw new ArgumentError(`Unsupported home-directory path: ${raw}`);\n    return path.resolve(raw);\n}\n","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/minimax/utils.js#L142-L178","documentation":"decodeAudioHex validates that hex-decoded audio matches the requested MP3 container: isMp3() accepts bytes starting with an 'ID3' tag or a valid MPEG frame sync (0xFF followed by 0xE0-masked byte). If neither signature is present, the library throws this CommandExecutionError because MiniMax returned audio that is not decodable MP3.","triggerScenarios":"Calling decodeAudioHex with format='mp3' while the decoded bytes lack an ID3 header and MPEG sync word — typically WAV (RIFF) bytes returned despite audio_setting.format='mp3', or corrupt/truncated data.","commonSituations":"Requesting mp3 but the MiniMax endpoint emits WAV; the hex payload is truncated so the first frame bytes are missing; intermediate proxies altering the payload.","solutions":["Check the request sets audio_setting.format='mp3' and regenerate","Inspect the first bytes of the decoded buffer — if they read 'RIFF'/'WAVE', switch to format='wav' and save with a .wav extension","Retry the API call; capture base_resp/trace_id if it recurs and contact MiniMax support","Validate data.audio hex integrity upstream (no truncation or whitespace) before decode"],"exampleFix":"// before\nconst bytes = decodeAudioHex(data.audio, expectedBytes, 'mp3'); // got RIFF bytes -> throws\n// after: align with the actual payload format\nif (bytes.subarray(0, 4).toString('ascii') === 'RIFF') {\n    fs.writeFileSync('out.wav', decodeAudioHex(data.audio, expectedBytes, 'wav'));\n}","handlingStrategy":"validation","validationCode":"function looksLikeMp3(hex) {\n    if (!/^[0-9a-f]+$/i.test(hex) || hex.length % 2 !== 0) return false;\n    const bytes = Buffer.from(hex, 'hex');\n    return bytes.length >= 2\n        && (bytes.subarray(0, 3).toString('ascii') === 'ID3'\n            || (bytes[0] === 0xff && (bytes[1] & 0xe0) === 0xe0));\n}\n// before decodeAudioHex(..., 'mp3'): if (!looksLikeMp3(data.audio)) switch format or retry","typeGuard":"function isMp3Buffer(bytes) {\n    return Buffer.isBuffer(bytes) && bytes.length >= 2\n        && (bytes.subarray(0, 3).toString('ascii') === 'ID3'\n            || (bytes[0] === 0xff && (bytes[1] & 0xe0) === 0xe0));\n}","tryCatchPattern":"try {\n    const bytes = decodeAudioHex(data.audio, expectedBytes, 'mp3');\n} catch (e) {\n    if (String(e.message).includes('not an MP3 file')) {\n        const wav = decodeAudioHex(data.audio, expectedBytes, 'wav');\n        fs.writeFileSync(out + '.wav', wav);\n    } else throw e;\n}","preventionTips":["Keep audio_setting.format in the request aligned with the extension you save","Validate ID3/frame-sync magic bytes before writing files","Retry generation once on format mismatch; persist trace_id for support","Add a unit test feeding a known WAV hex payload to catch regressions"],"tags":["minimax","audio","mp3","format-validation"],"backgroundTag":"unexpected-audio-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}