{"record":{"id":"2c2daa3735cdca07","repo":"decolua/9router","slug":"fusion-panel-model-returned-empty-content","errorCode":null,"errorMessage":"FUSION Panel ${model} returned empty content","messagePattern":"FUSION Panel (.+?) returned empty content","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"open-sse/services/combo.js","lineNumber":601,"sourceCode":"  log.info(\"FUSION\", `fan-out collected in ${Date.now() - t0}ms`);\n\n  // 2. Collect successful answers.\n  const answers = [];\n  for (let i = 0; i < settled.length; i++) {\n    const res = settled[i];\n    const model = panel[i];\n    if (!res) { log.warn(\"FUSION\", `Panel ${model} dropped (straggler/timeout)`); continue; }\n    if (res.__timeout) { log.warn(\"FUSION\", `Panel ${model} timed out`); continue; }\n    if (res.__error) { log.warn(\"FUSION\", `Panel ${model} threw`, { error: res.__error?.message || String(res.__error) }); continue; }\n    if (!res.ok) { log.warn(\"FUSION\", `Panel ${model} failed`, { status: res.status }); continue; }\n    try {\n      const json = await res.clone().json();\n      const text = extractPanelText(json);\n      if (text) {\n        answers.push({ model, text });\n        log.info(\"FUSION\", `Panel ${model} ok (${text.length} chars)`);\n      } else {\n        log.warn(\"FUSION\", `Panel ${model} returned empty content`);\n      }\n    } catch (e) {\n      log.warn(\"FUSION\", `Panel ${model} unparseable`, { error: e.message || String(e) });\n    }\n  }\n\n  // 3. Degrade gracefully when the panel is too thin to fuse.\n  if (answers.length === 0) {\n    log.warn(\"FUSION\", \"All panel models failed\");\n    return new Response(\n      JSON.stringify({ error: { message: \"All fusion panel models failed\" } }),\n      { status: 503, headers: { \"Content-Type\": \"application/json\" } }\n    );\n  }\n  if (answers.length === 1) {\n    log.info(\"FUSION\", `Only ${answers[0].model} succeeded — answering directly (no fusion)`);\n    return handleSingleModel(body, answers[0].model);\n  }","sourceCodeStart":583,"sourceCodeEnd":619,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/services/combo.js#L583-L619","documentation":"After a panel model returns a 2xx response, the FUSION service parses its JSON and extracts text via extractPanelText. If the payload parses but contains no usable content (empty choices/message content, whitespace-only text), the panel is skipped with this warning and excluded from fusion.","triggerScenarios":"Upstream returned 200 with an empty body content — e.g. the model returned only a refusal stripped of content, a filter removed the output, max_tokens was set so low the completion is empty, or a provider returns a nonstandard success shape extractPanelText doesn't recognize.","commonSituations":"max_tokens=1 or tiny budget yields empty text; safety filters blank the content; a translator maps the provider's response into a shape where content lands in a field extractPanelText doesn't read (nonstandard model); streaming-only endpoints return an empty non-stream body.","solutions":["Inspect the panel's raw response (enable debug logging) to see where the content actually sits","Raise max_tokens for the request so the model can produce non-empty output","Confirm the model returns standard OpenAI-style choices[0].message.content, or add a direct translator for that provider","Retry the prompt — one-off empty content (safety filter) usually resolves with rephrasing","Check whether a content filter on the provider account is suppressing output"],"exampleFix":"// before\ndefaults: { max_tokens: 1 }\n// after\ndefaults: { max_tokens: 4096 }","handlingStrategy":"validation","validationCode":"const json = await res.clone().json();\nconst text = extractPanelText(json);\nif (typeof text !== 'string' || !text.trim()) throw new Error(`panel ${model} returned empty content`);","typeGuard":"const hasContent = (json) =>\n  typeof json?.choices?.[0]?.message?.content === 'string' &&\n  json.choices[0].message.content.trim().length > 0;","tryCatchPattern":"try {\n  const text = extractPanelText(await res.clone().json());\n  if (!text) throw new Error('empty content');\n  return text;\n} catch (e) {\n  console.warn(`panel unusable: ${e.message}`); // excluded from fusion, request still served\n  return null;\n}","preventionTips":["Set a sane max_tokens floor so completions are never truncated to empty","Rephrase prompts that trip provider safety filters","Use direct translators for nonstandard providers so content lands where extractPanelText reads","Log the raw JSON when extraction fails to catch provider shape drift early"],"tags":["fusion","empty-response","response-parsing"],"backgroundTag":"empty-model-response","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}