{"record":{"id":"0fbd7944bb3477aa","repo":"jackwener/OpenCLI","slug":"minimax-music-returned-invalid-hexadecimal-audio","errorCode":null,"errorMessage":"MiniMax music returned invalid hexadecimal audio","messagePattern":"MiniMax music returned invalid hexadecimal audio","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/minimax/utils.js","lineNumber":150,"sourceCode":"    return extraInfo.music_size;\n}\n\nexport function requireAudioUrl(value) {\n    let url;\n    try {\n        url = new URL(value);\n    } catch {\n        throw new CommandExecutionError('MiniMax music returned data.audio that is not a URL');\n    }\n    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}","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/minimax/utils.js#L132-L168","documentation":"decodeAudioHex validates the hex payload before decoding: it must be non-empty, have even length (each byte is two hex digits), and contain only hex characters. Any violation throws, since Buffer.from(value, 'hex') would otherwise silently produce truncated or garbage bytes.","triggerScenarios":"data.audio hex string is empty; it has odd length (a character lost to truncation or encoding corruption); it contains non-hex characters (whitespace, '0x' prefix, base64 characters, or a URL pasted into the hex path).","commonSituations":"HTTP client truncating large response bodies; calling the hex decoder with a URL value from a URL-returning endpoint; copying audio strings through a shell or editor that strips characters; MiniMax returning base64 instead of hex after an API change.","solutions":["Print the first/last few characters and the length of data.audio to spot truncation or a '0x' prefix/base64 content","If the value is a URL, use requireAudioUrl/download instead of decodeAudioHex","If it looks like base64, decode with Buffer.from(value, 'base64') instead","Increase any HTTP body size limits in your HTTP client so large hex payloads are not truncated"],"exampleFix":"// before: decoding whatever arrives\nconst bytes = decodeAudioHex(data.audio, expectedBytes, format);\n// after: detect base64/hex form first\nconst isHex = data.audio.length % 2 === 0 && /^[0-9a-f]+$/i.test(data.audio);\nconst bytes = isHex\n  ? decodeAudioHex(data.audio, expectedBytes, format)\n  : decodeAudioBase64(data.audio, expectedBytes, format);","handlingStrategy":"validation","validationCode":"function looksLikeHex(v) {\n  return typeof v === 'string' && v.length > 0 && v.length % 2 === 0 && /^[0-9a-f]+$/i.test(v);\n}\nif (!looksLikeHex(payload?.data?.audio)) {\n  // choose URL or base64 path, or inspect raw payload\n}","typeGuard":"function isHexString(v) {\n  return typeof v === 'string' && v.length % 2 === 0 && /^[0-9a-f]+$/i.test(v);\n}","tryCatchPattern":"try {\n  const bytes = decodeAudioHex(value, expectedBytes, format);\n} catch (e) {\n  if (/invalid hexadecimal/.test(e.message)) {\n    if (/^[A-Za-z0-9+/=]+$/.test(value)) {\n      // looks like base64 — decode accordingly\n    } else {\n      console.error('audio value head:', value.slice(0, 32), 'len:', value.length);\n    }\n  } else throw e;\n}","preventionTips":["Raise HTTP client max body size to avoid truncation of large hex payloads","Strip whitespace/'0x' prefixes before decoding","Detect payload encoding (hex vs base64 vs URL) before dispatching","Never pass audio strings through lossy shell/editor pipelines"],"tags":["minimax","music","hex","encoding","audio"],"backgroundTag":"invalid-encoding","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}