{"record":{"id":"02a1049adc0f6222","repo":"chatboxai/chatbox","slug":"json-stringify-json","errorCode":null,"errorMessage":"JSON.stringify(json)","messagePattern":"JSON\\.stringify\\(json\\)","errorType":"http","errorClass":"ApiError","httpStatus":null,"severity":"error","filePath":"src/shared/models/openai-compatible.ts","lineNumber":159,"sourceCode":") {\n  const headers = {\n    Authorization: `Bearer ${params.apiKey}`,\n    ...(params.extraHeaders || {}),\n  }\n  const response = params.customFetch\n    ? await params.customFetch(`${params.apiHost}/models`, {\n        method: 'GET',\n        headers,\n      })\n    : await dependencies.request.apiRequest({\n        url: `${params.apiHost}/models`,\n        method: 'GET',\n        headers,\n        useProxy: params.useProxy,\n      })\n  const json: ListModelsResponse = await response.json()\n  if (!json.data) {\n    throw new ApiError(JSON.stringify(json))\n  }\n  return json.data.map((item) => {\n    const modelInfo: ProviderModelInfo = {\n      modelId: item.id,\n      type: 'chat',\n    }\n\n    // Add nickname from OpenRouter name field\n    if (item.name) {\n      modelInfo.nickname = item.name\n    }\n\n    // Add context window if available\n    if (item.context_length) {\n      modelInfo.contextWindow = item.context_length\n    }\n\n    // Add capabilities based on architecture","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/shared/models/openai-compatible.ts#L141-L177","documentation":"Thrown by OpenAICompatibleSDK.listModels when the GET {apiHost}/models response JSON lacks a `data` array. The entire parsed JSON is JSON.stringify'd into the ApiError message so the caller sees the unexpected payload. This is a contract violation: the OpenAI-compatible /models endpoint must return { data: [{ id }, ...] }.","triggerScenarios":"response.json() resolves to an object without a `data` field — e.g. the endpoint returned an error envelope ({ error: {...} }), an HTML page parsed as empty, an auth challenge, or a non-OpenAI schema (Anthropic-style, Google-style).","commonSituations":"Wrong apiHost (pointed at a non-OpenAI-compatible API); missing/invalid API key causing a JSON error body; endpoint requires a different path (e.g. /v1/models vs /models); self-hosted server returns its own error shape; apiHost includes a trailing slash doubling the path.","solutions":["Verify apiHost points to an OpenAI-compatible /models endpoint and the API key has permission to list models.","Open apiHost/models in a browser/curl and confirm the response shape is { data: [{ id: '...' }, ...] }; if not, switch provider type or fix the host.","Remove trailing slashes and duplicate '/v1' segments in apiHost.","Improve the error to surface response.status alongside the body so auth vs. shape issues are distinguishable."],"exampleFix":"// before\nconst json: ListModelsResponse = await response.json()\nif (!json.data) {\n  throw new ApiError(JSON.stringify(json))\n}\n// after — include status and a hint\nconst json: ListModelsResponse = await response.json()\nif (!json.data) {\n  throw new ApiError(`listModels: no 'data' field (status ${response.status}): ${JSON.stringify(json)}`)\n}","handlingStrategy":"validation","validationCode":"// Smoke-test the /models endpoint shape before relying on it.\nasync function modelsEndpointIsOpenAICompatible(apiHost: string, headers: Record<string,string>): Promise<boolean> {\n  try {\n    const res = await fetch(`${apiHost.replace(/\\/$/, '')}/models`, { headers })\n    const json = await res.json().catch(() => null)\n    return Boolean(json && Array.isArray(json.data))\n  } catch { return false }\n}","typeGuard":"function isListModelsResponse(v: unknown): v is { data: { id: string }[] } {\n  return typeof v === 'object' && v !== null && Array.isArray((v as any).data)\n}","tryCatchPattern":"try {\n  return await sdk.listModels()\n} catch (e) {\n  if (e instanceof ApiError) {\n    // e.message is JSON.stringify(json); parse it back to inspect\n    const body = JSON.parse(e.message)\n    if (body.error) showUser(`Provider error: ${body.error.message ?? body.error}`)\n    else showUser('Unexpected /models response — check apiHost and API key.')\n    return []\n  }\n  throw e\n}","preventionTips":["Confirm apiHost points to an OpenAI-compatible API and includes the correct path prefix (/v1).","Remove trailing slashes and duplicate /v1 segments in apiHost.","Validate the API key has permission to list models before calling."],"tags":["openai-compatible","list-models","api-contract","config"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}