{"record":{"id":"e89d75d32a6aeffe","repo":"janhq/jan","slug":"friendly-requesturlof-input","errorCode":null,"errorMessage":"${friendly} (${requestUrlOf(input)})","messagePattern":"\\$\\{friendly\\} \\(\\$\\{requestUrlOf\\(input\\)\\}\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"web-app/src/lib/model-factory.ts","lineNumber":538,"sourceCode":"    let rawBody: Record<string, unknown> | null = null\n    if (init?.method === 'POST' || !init?.method) {\n      try {\n        rawBody = init?.body ? JSON.parse(init.body as string) : {}\n      } catch (e) {\n        throw new Error(\n          `Failed to parse request body as JSON: ${e instanceof Error ? e.message : String(e)}`\n        )\n      }\n      init = { ...init, body: JSON.stringify(buildBody(rawBody!, true)) }\n    }\n\n    let res: Response\n    try {\n      res = await baseFetch(input, init)\n    } catch (err) {\n      const friendly = describeTransportError(err)\n      if (!friendly) throw err\n      throw new Error(`${friendly} (${requestUrlOf(input)})`)\n    }\n    if (res.ok) {\n      // OpenAI-compatible servers may interleave custom named SSE events (e.g.\n      // tool-progress) with chat.completion.chunk data; the AI SDK validates\n      // every data line against the chunk schema, so strip non-default events.\n      // Opt-in only: Anthropic and the OpenAI Responses API use named SSE\n      // events as their protocol, so filtering there blanks the whole stream.\n      const contentType = res.headers.get('content-type') || ''\n      if (\n        filterNamedSseEvents &&\n        res.body &&\n        contentType.includes('text/event-stream')\n      ) {\n        return new Response(filterDefaultSseEvents(res.body), {\n          status: res.status,\n          statusText: res.statusText,\n          headers: res.headers,\n        })","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/janhq/jan/blob/7205d770c1e097c3daf35a911176410e93bc5564/web-app/src/lib/model-factory.ts#L520-L556","documentation":"In createCustomFetch, when baseFetch itself rejects (before any HTTP response), describeTransportError converts the low-level error into a friendly message and rethrows it with the request URL appended in parentheses. If the error is not a recognized transport failure it is rethrown unchanged. This surfaces DNS failures, refused connections, TLS errors, and aborts with context about which endpoint failed.","triggerScenarios":"Base URL host unreachable or DNS failing; server process not listening on the port; TLS certificate errors; network offline; request aborted mid-flight when the underlying error maps to a known transport cause.","commonSituations":"Local inference server (llama.cpp/MLX session) not started; typo'd base_url like http://localhost:808 vs :8080; CORS/proxy interception; firewall or VPN blocking the endpoint; Docker networking misconfig.","solutions":["Check the URL in the error message and verify the server is running and reachable (curl the base URL).","Fix the provider base_url in Settings > Model Providers (correct host/port/scheme).","Inspect the underlying cause: start the model server session, fix TLS/proxy settings, or retry after connectivity is restored."],"exampleFix":"// before\nprovider.base_url = 'http://localhost:808'\n// after\nprovider.base_url = 'http://localhost:8080' // server actually listening here","handlingStrategy":"retry","validationCode":"const url = new URL(provider.base_url)\nif (!['http:', 'https:'].includes(url.protocol)) throw new Error('base_url must be http(s)')","typeGuard":"const isTransportError = (e) => e instanceof TypeError || ['ECONNREFUSED','ENOTFOUND','ECONNRESET','fetch failed'].some(s => String(e?.cause ?? e).includes(s))","tryCatchPattern":"try { await fetchChat(url, init) } catch (e) { if (/\\(http/.test(e.message)) { await backoffRetry(() => fetchChat(url, init), 3); } else throw e }","preventionTips":["Health-check the base_url with a cheap GET before chat calls.","Pin correct ports and schemes in provider settings; lint base_url values.","Wrap all provider fetches in a retry-with-backoff helper for transient transport errors."],"tags":["network","fetch","connection","transport"],"backgroundTag":"connection-refused","analyzedSha":"7205d770c1e097c3daf35a911176410e93bc5564","analyzedAt":"2026-09-17T14:27:30.100Z","contentChangedAt":"2026-09-17T14:27:30.100Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}