Crosstalk-Solutions/project-nomad · error · Error

HTTP ${resp.status} from ${MANIFEST_URL}

Error message

HTTP ${resp.status} from ${MANIFEST_URL}

What it means

Thrown by DrugLabelManifestFetcher-like fetchManifest() when fetching MANIFEST_URL returns a non-2xx HTTP status. The fetch itself succeeded at the transport level, but the FDA endpoint responded with an error status, so the manifest JSON is never parsed.

Source

Thrown at admin/app/jobs/download_drug_data_job.ts:312

  }

  private async fetchManifest(): Promise<DrugLabelManifest> {
    return DownloadDrugDataJob.fetchManifest()
  }

  /**
   * Fetch + parse the openFDA download manifest. The SINGLE source of truth for
   * the openFDA manifest call (Maxim 4): the download job uses it on pass 0, and
   * the freshness check (DrugReferenceService.checkForUpdate, driven by
   * attemptAutoUpdate) reuses it so there is exactly one place that knows the URL and the
   * offline-error translation.
   */
  static async fetchManifest(): Promise<DrugLabelManifest> {
    let json: unknown
    try {
      const resp = await fetch(MANIFEST_URL)
      if (!resp.ok) {
        throw new Error(`HTTP ${resp.status} from ${MANIFEST_URL}`)
      }
      json = await resp.json()
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err)
      if (
        msg.includes('ENOTFOUND') ||
        msg.includes('ECONNREFUSED') ||
        msg.includes('ECONNRESET') ||
        msg.includes('fetch failed')
      ) {
        throw new Error(`No internet — connect to download FDA drug data. (${msg})`)
      }
      throw err
    }
    return parseDrugLabelManifest(json)
  }

  private async writeDownloadState(

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Retry after a short wait — transient 5xx/429 from the FDA API usually clears.
  2. Check the URL in a browser/curl: curl -i $MANIFEST_URL to see the actual status body.
  3. If 404/410, verify the manifest endpoint hasn't changed in the FDA openfda/drug-labels API docs.
  4. If 429, add backoff between job runs or reduce download frequency.

Example fix

// before
const resp = await fetch(MANIFEST_URL)
if (!resp.ok) throw new Error(`HTTP ${resp.status} from ${MANIFEST_URL}`)

// after — retry with backoff on transient statuses
async function fetchManifestRetry(retries = 3) {
  for (let i = 0; i <= retries; i++) {
    const resp = await fetch(MANIFEST_URL)
    if (resp.ok) return parseDrugLabelManifest(await resp.json())
    if (resp.status >= 500 || resp.status === 429) {
      await new Promise(r => setTimeout(r, 2 ** i * 1000))
      continue
    }
    throw new Error(`HTTP ${resp.status} from ${MANIFEST_URL}`)
  }
  throw new Error(`HTTP retries exhausted for ${MANIFEST_URL}`)
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight reachability (optional, cheap)
const resp = await fetch(MANIFEST_URL, { method: 'HEAD' }).catch(() => null)
if (!resp?.ok) { /* defer the job instead of failing */ }

Try / catch

catch (err) {
  if (err instanceof Error && /^HTTP \d+ from /.test(err.message)) {
    const status = Number(err.message.match(/^HTTP (\d+)/)?.[1])
    if (status >= 500 || status === 429) return retryWithBackoff()
  }
  throw err
}

Prevention

When it happens

Trigger: fetch(MANIFEST_URL) resolves with resp.ok false: FDA API is down or returning 5xx, rate limiting (429), moved endpoint (404/301), proxy or captive portal returning 4xx.

Common situations: Temporary FDA service outage, rate limit hit during repeated testing, changed/retired endpoint path, corporate proxy intercepting the request, DNS hijack returning an error page.

Related errors


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/02fd54d9b15e0d76. Report an issue: GitHub.