{"record":{"id":"91452aae0a240e8e","repo":"decolua/9router","slug":"minimax-tts-error-res-status","errorCode":null,"errorMessage":"MiniMax TTS error (${res.status})","messagePattern":"MiniMax TTS error \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/handlers/ttsProviders/minimax.js","lineNumber":49,"sourceCode":"        bitrate: 128000,\n        format: \"mp3\",\n        channel: 1,\n      },\n    }),\n  });\n\n  const rawText = await res.text();\n  let data = {};\n  if (rawText) {\n    try { data = JSON.parse(rawText); } catch { data = {}; }\n  }\n\n  const baseResp = data.base_resp || data.baseResp || {};\n  const statusCode = Number(baseResp.status_code ?? baseResp.statusCode ?? 0);\n  const statusMessage = baseResp.status_msg || baseResp.statusMsg || data.message || \"\";\n\n  if (!res.ok) {\n    throw new Error(statusMessage || rawText || `MiniMax TTS error (${res.status})`);\n  }\n  if (statusCode !== 0) {\n    throw new Error(statusMessage || \"MiniMax TTS upstream error\");\n  }\n\n  return {\n    base64: hexToBase64(data.data?.audio),\n    format: data.extra_info?.audio_format || data.extraInfo?.audioFormat || \"mp3\",\n  };\n}\n","sourceCodeStart":31,"sourceCodeEnd":60,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/ttsProviders/minimax.js#L31-L60","documentation":"When the MiniMax T2A HTTP call returns a non-2xx status, the handler prefers the in-body `base_resp.status_msg`, then the raw response text, and finally falls back to this generic message containing the HTTP status. It means the upstream rejected the request (auth, quota, invalid params, etc.) and no usable error text was extracted.","triggerScenarios":"MiniMax returns 401 (invalid/expired API key), 429 (rate limit), 400 (bad model/voice/text), or 5xx — and `base_resp.status_msg`, `statusMsg`, `data.message`, and the raw text are all empty so only the status-templated message remains.","commonSituations":"Expired or wrong-region MiniMax API key (401); exhausted free quota (429); group_id missing from the URL (400); MiniMax outage (5xx).","solutions":["Read `res.status` from the message: 401 → fix the API key; 429 → backoff/quota; 400 → validate modelId/voiceId/text and the group_id query param","Log `rawText` (the response body) to surface MiniMax's real error detail, since the generic fallback means the body was empty or unparseable","Check `base_resp.status_code` in the body even alongside non-2xx statuses for MiniMax's own error codes","Retry with exponential backoff for 5xx/429; do not retry 4xx client errors"],"exampleFix":"// before\nthrow new Error(statusMessage || rawText || `MiniMax TTS error (${res.status})`);\n// after\nthrow new Error(statusMessage || rawText || `MiniMax TTS error (${res.status})`);\n// plus: log rawText once at source so the generic fallback is never the only signal\nconsole.error('[minimax-tts] raw body:', rawText);","handlingStrategy":"retry","validationCode":"if (!apiKey) throw new Error('MiniMax API key missing');\nif (!text?.trim()) throw new Error('text required');","typeGuard":"const isMiniMaxErrorBody = (d) => d != null && typeof d === 'object' && d.base_resp != null && typeof d.base_resp.status_code === 'number';","tryCatchPattern":"try { return await minimaxTts(...); } catch (e) { const m = e.message.match(/MiniMax TTS error \\((\\d+)\\)/); const code = m && parseInt(m[1]); if (code === 429 || (code && code >= 500)) { await sleep(backoff); return retry(); } throw e; }","preventionTips":["Store and rotate the MiniMax API key properly; include group_id in the request URL","Apply exponential backoff only for 429/5xx; fail fast on 4xx","Log raw response bodies so the generic fallback message never hides the real cause"],"tags":["network","http","tts","minimax","upstream"],"backgroundTag":"upstream-api-error","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}