{"record":{"id":"00e9ec3297d92230","repo":"nexu-io/open-design","slug":"openai-non-json-response-truncate-text-200","errorCode":null,"errorMessage":"openai non-JSON response: ${truncate(text, 200)}","messagePattern":"openai non-JSON response: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/media/index.ts","lineNumber":959,"sourceCode":"\n  const resp = await fetch(url, withMediaRequestInit(ctx, {\n    method: 'POST',\n    headers,\n    body: JSON.stringify(body),\n    dispatcher: ctx.requestInit.dispatcher\n      ?? openAIImageDispatcher as unknown as NonNullable<RequestInit['dispatcher']>,\n    signal: AbortSignal.timeout(Math.max(OPENAI_IMAGE_HEADERS_TIMEOUT_MS, OPENAI_IMAGE_BODY_TIMEOUT_MS)),\n  }));\n  const text = await resp.text();\n  if (!resp.ok) {\n    const tag = azure ? 'azure-openai' : 'openai';\n    throw new Error(`${tag} ${resp.status}: ${truncate(text, 240)}`);\n  }\n  let data: any;\n  try {\n    data = JSON.parse(text);\n  } catch {\n    throw new Error(`openai non-JSON response: ${truncate(text, 200)}`);\n  }\n  const entry = data && Array.isArray(data.data) ? data.data[0] : null;\n  if (!entry) throw new Error('openai response had no data[0]');\n  let bytes;\n  if (entry.b64_json) {\n    bytes = Buffer.from(entry.b64_json, 'base64');\n  } else if (entry.url) {\n    const imgResp = await fetch(entry.url, withMediaRequestInit(ctx));\n    if (!imgResp.ok) throw new Error(`openai image fetch ${imgResp.status}`);\n    const arr = await imgResp.arrayBuffer();\n    bytes = Buffer.from(arr);\n  } else {\n    throw new Error('openai response had neither b64_json nor url');\n  }\n\n  const tag = azure ? 'azure-openai' : 'openai';\n  return {\n    bytes,","sourceCodeStart":941,"sourceCodeEnd":977,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/media/index.ts#L941-L977","documentation":"Thrown by renderOpenAIImage when resp.ok is true but JSON.parse(text) throws. The dispatcher assumes a JSON images/generations response; a non-JSON 200 usually means a man-in-the-middle (corporate proxy, captive portal, service mesh) returned an HTML page with a 200 status, or the baseUrl was pointed at an endpoint that does not speak the OpenAI schema.","triggerScenarios":"Custom baseUrl pointed at a browser-facing gateway that returns HTML; corporate TLS-intercepting proxy injecting a block page; misconfigured reverse proxy returning a health-check page; baseUrl pointing at /v1 instead of the host root.","commonSituations":"Self-hosted gateway in front of OpenAI that 200s with HTML on unknown paths; Azure app-gateway rewrite rule stripping the body; local mock server returning text/plain.","solutions":["Inspect the truncated body in the message to identify what returned the non-JSON response (proxy page, gateway error, etc.).","Verify credentials.baseUrl resolves to a real OpenAI/Azure endpoint and not a UI or docs host.","Bypass or reconfigure the intercepting proxy for the OpenAI host, or point baseUrl at the proxy's true API path.","If running a custom OpenAI-compatible server, ensure it returns application/json with the {data:[...]} schema on success."],"exampleFix":"// before\nOD_OPENAI_BASE_URL=https://gateway.internal/\n// after (point at the API path the gateway actually serves)\nOD_OPENAI_BASE_URL=https://gateway.internal/v1","handlingStrategy":"try-catch","validationCode":"function isOpenAIJsonEndpoint(baseUrl: string): boolean {\n  // crude guard: must look like an API host, not a UI/docs host\n  return /^https:\\/\\/[\\w.-]+\\.openai\\.com(?:\\/.*)?$/.test(baseUrl)\n    || /\\/v1$/.test(baseUrl)\n    || /openai\\.azure\\.com/.test(baseUrl);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await renderOpenAIImage(ctx, credentials);\n} catch (err) {\n  const m = err instanceof Error ? err.message : '';\n  if (m.startsWith('openai non-JSON response:')) {\n    // surface the body snippet to the user — almost always a proxy/gateway issue\n    throw new ConfigError(`OpenAI returned HTML/text, not JSON. Check baseUrl and proxies. Body: ${m}`);\n  }\n  throw err;\n}","preventionTips":["Point baseUrl at the API host (e.g. https://api.openai.com/v1), never at a UI/docs host.","Bypass TLS-intercepting proxies for the OpenAI host, or whitelist the API path on the proxy.","Smoke-test the endpoint with curl before configuring the daemon."],"tags":["network","media","openai","response-shape","proxy"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}