{"record":{"id":"3c553e5c54e53184","repo":"Crosstalk-Solutions/project-nomad","slug":"no-internet-connect-to-download-fda-drug-data","errorCode":null,"errorMessage":"No internet — connect to download FDA drug data. (${msg})","messagePattern":"No internet — connect to download FDA drug data\\. \\((.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"admin/app/jobs/download_drug_data_job.ts","lineNumber":323,"sourceCode":"   * offline-error translation.\n   */\n  static async fetchManifest(): Promise<DrugLabelManifest> {\n    let json: unknown\n    try {\n      const resp = await fetch(MANIFEST_URL)\n      if (!resp.ok) {\n        throw new Error(`HTTP ${resp.status} from ${MANIFEST_URL}`)\n      }\n      json = await resp.json()\n    } catch (err) {\n      const msg = err instanceof Error ? err.message : String(err)\n      if (\n        msg.includes('ENOTFOUND') ||\n        msg.includes('ECONNREFUSED') ||\n        msg.includes('ECONNRESET') ||\n        msg.includes('fetch failed')\n      ) {\n        throw new Error(`No internet — connect to download FDA drug data. (${msg})`)\n      }\n      throw err\n    }\n    return parseDrugLabelManifest(json)\n  }\n\n  private async writeDownloadState(\n    manifest: DrugLabelManifest,\n    totalParts: number,\n    parts: DownloadStateMarker['parts']\n  ): Promise<void> {\n    const KVStore = (await import('#models/kv_store')).default\n    const marker: DownloadStateMarker = {\n      export_date: manifest.export_date,\n      totalParts,\n      totalRecords: manifest.total_records,\n      parts,\n      completedAtMs: Date.now(),","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/Crosstalk-Solutions/project-nomad/blob/0bd1c6f4f9888d577fe232de06ac144bb8337131/admin/app/jobs/download_drug_data_job.ts#L305-L341","documentation":"Thrown by fetchManifest() when the underlying network error message matches known offline signatures: ENOTFOUND, ECONNREFUSED, ECONNRESET, or 'fetch failed'. It converts the raw Node fetch failure into a user-friendly 'no internet' error before rethrowing non-network errors unchanged.","triggerScenarios":"DNS resolution failure (ENOTFOUND), connection refused by firewall/proxy (ECONNREFUSED), dropped connection (ECONNRESET), or undici's generic 'fetch failed' wrapper when the machine has no route to the FDA host.","commonSituations":"Device offline when the scheduled download job runs, firewall blocking outbound HTTPS, misconfigured DNS, VPN dropped mid-request, air-gapped or lab environment.","solutions":["Verify connectivity: ping api.fda.gov or curl -I https://api.fda.gov and restore network/DNS.","Check firewall/proxy rules allowing HTTPS to the FDA host; set HTTPS_PROXY if a proxy is required.","If running on a schedule, ensure the device is online at job time or pause the scheduler when offline.","Re-run the Download FDA data job once connectivity is confirmed."],"exampleFix":"// before\nthrow new Error(`No internet — connect to download FDA drug data. (${msg})`)\n\n// after — surface the original cause for diagnostics\nthrow new Error(`No internet — connect to download FDA drug data. (${msg})`, { cause: err })","handlingStrategy":"retry","validationCode":"import { lookup } from 'node:dns/promises'\nasync function online(host: string): Promise<boolean> {\n  try { await lookup(host); return true } catch { return false }\n}","typeGuard":null,"tryCatchPattern":"catch (err) {\n  if (err instanceof Error && err.message.includes('No internet')) {\n    // pause scheduler, resume on 'online' event; safe to retry later\n  }\n  throw err\n}","preventionTips":["Gate scheduled downloads on connectivity checks or OS online events.","Keep the last successful dataset so the app degrades gracefully offline.","Surfacing this as a UI banner rather than a job failure improves UX."],"tags":["network","offline","dns","download-job"],"backgroundTag":"network-unreachable","analyzedSha":"0bd1c6f4f9888d577fe232de06ac144bb8337131","analyzedAt":"2026-08-27T05:34:15.424Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}