{"record":{"id":"c3c7b979b0ed9555","repo":"decolua/9router","slug":"minimax-tts-returned-invalid-audio","errorCode":null,"errorMessage":"MiniMax TTS returned invalid audio","messagePattern":"MiniMax TTS returned invalid audio","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/handlers/ttsProviders/minimax.js","lineNumber":7,"sourceCode":"import { Buffer } from \"node:buffer\";\n\nfunction hexToBase64(audioHex) {\n  const clean = typeof audioHex === \"string\" ? audioHex.trim() : \"\";\n  if (!clean) throw new Error(\"MiniMax TTS returned no audio\");\n  if (clean.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(clean)) {\n    throw new Error(\"MiniMax TTS returned invalid audio\");\n  }\n  return Buffer.from(clean, \"hex\").toString(\"base64\");\n}\n\n// MiniMax T2A HTTP: returns hex-encoded audio in non-streaming mode.\nexport default async function minimaxTts({ baseUrl, apiKey, text, modelId, voiceId }) {\n  const res = await fetch(baseUrl, {\n    method: \"POST\",\n    headers: { \"Content-Type\": \"application/json\", \"Authorization\": `Bearer ${apiKey}` },\n    body: JSON.stringify({\n      model: modelId || \"speech-2.8-hd\",\n      text,\n      stream: false,\n      language_boost: \"auto\",\n      output_format: \"hex\",\n      voice_setting: {\n        voice_id: voiceId || \"English_expressive_narrator\",\n        speed: 1,","sourceCodeStart":1,"sourceCodeEnd":25,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/ttsProviders/minimax.js#L1-L25","documentation":"`hexToBase64` validates the MiniMax audio hex string before decoding: it must have even length and contain only hex characters `[0-9a-f]`. Otherwise it throws this error. It means audio data was present but malformed as hex.","triggerScenarios":"The audio field contains a non-hex string — truncated hex (odd length), a URL/base64-encoded value instead of hex, error text placed in the audio field, or double-encoded data.","commonSituations":"A MiniMax API format change (audio now returned base64, not hex); response truncation by a proxy; passing an already-base64 payload into the hex path by wiring the wrong field; protocol migration between API versions.","solutions":["Inspect the actual audio value — if it looks base64, use it directly instead of hex-decoding","Check for odd-length truncation (proxy/timeout cutting the response) and retry","Verify the MiniMax API version/model docs for the current audio encoding field (`audio_hex` vs `audio`)","Sanitize: strip whitespace/`0x` prefixes and validate the full string with /^[0-9a-f]+$/i before decoding"],"exampleFix":"// before\nconst b64 = Buffer.from(clean, 'hex').toString('base64');\n// after (handle base64-encoded responses too)\nconst b64 = /^[0-9a-f]+$/i.test(clean) && clean.length % 2 === 0\n  ? Buffer.from(clean, 'hex').toString('base64')\n  : clean; // already base64","handlingStrategy":"type-guard","validationCode":"const audio = data?.data?.audio;\nif (audio && !/^[0-9a-f]+$/.test(audio.trim())) console.warn('MiniMax audio is not hex — check API version');","typeGuard":"const isEvenLenHex = (v) => typeof v === 'string' && v.length > 0 && v.length % 2 === 0 && /^[0-9a-f]+$/i.test(v);","tryCatchPattern":"try { return await minimaxTts(...); } catch (e) { if (e.message === 'MiniMax TTS returned invalid audio') { logger.warn('non-hex audio payload — treating as base64 or failing over'); return fallbackTts(text); } throw e; }","preventionTips":["Pin the MiniMax API version and diff responses after upgrades","Guard against proxy truncation (timeouts) that yields odd-length hex","Detect base64-vs-hex payloads before decoding"],"tags":["tts","minimax","encoding","validation","hex"],"backgroundTag":"invalid-encoding","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}