{"record":{"id":"d6e455043daecacc","repo":"koala73/worldmonitor","slug":"http-response-status-weather-alert-select","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/_weather-alert-select.mjs","lineNumber":838,"sourceCode":"  allowedHosts,\n  maxBytes = ECCC_MAX_BYTES,\n  fetchFn = globalThis.fetch,\n  userAgent = CHROME_UA,\n  timeoutMs = 15_000,\n  accept = 'application/geo+json',\n  byteBudget,\n} = {}) {\n  const parsed = new URL(url);\n  const allowed = new Set((allowedHosts || []).map((host) => String(host).toLowerCase()));\n  if (parsed.protocol !== 'https:' || !allowed.has(parsed.hostname.toLowerCase())) {\n    throw new Error('UNTRUSTED_SOURCE_HOST');\n  }\n  const response = await fetchFn(parsed.toString(), {\n    headers: { Accept: accept, 'User-Agent': userAgent },\n    redirect: 'error',\n    signal: AbortSignal.timeout(timeoutMs),\n  });\n  if (!response.ok) throw new Error(`HTTP ${response.status}`);\n  return readResponseLimited(response, maxBytes, byteBudget);\n}\n","sourceCodeStart":820,"sourceCodeEnd":841,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/_weather-alert-select.mjs#L820-L841","documentation":"Generic non-2xx guard for the ECCC fetch wrapper: after issuing the request (with Accept header, User-Agent, redirect:'error', and an AbortSignal timeout), any response whose `ok` is false raises `HTTP <status>`. It surfaces the raw HTTP status so callers can distinguish 404/429/5xx outcomes of the alert fetch.","triggerScenarios":"The ECCC alerts endpoint (or any URL passed through this fetch helper) responds with a status outside 200-299 — e.g. 404 after a schema/URL change, 429 rate limiting, 500/503 server-side outage.","commonSituations":"ECCC renames or versions an endpoint; the client is rate-limited for polling too aggressively or missing an acceptable User-Agent; upstream returns 503 during maintenance; a redirect is attempted but redirect:'error' converts it into a 3xx failure surfaced as HTTP 3xx.","solutions":["Log/inspect the numeric status: 404 means fix the endpoint URL; 429 means add backoff/spacing; 5xx means retry later","Add retry with exponential backoff for transient 429/5xx responses before failing the whole fetch","Confirm the request URL, query parameters, and Accept header still match the current ECCC API contract","Keep a valid descriptive User-Agent (per repo policy) since some upstreams reject requests without one"],"exampleFix":"// before\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n// after\nif (!response.ok) {\n  if (response.status === 429 || response.status >= 500) {\n    await sleep(backoffMs);\n    return fetchWithRetry(url, opts, attempt + 1);\n  }\n  throw new Error(`HTTP ${response.status}`);\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  const data = await fetchEcccJson(url);\n} catch (err) {\n  const m = /^HTTP (\\d+)$/.exec(err.message);\n  if (m && (m[1] === '429' || m[1].startsWith('5'))) {\n    await backoffAndRetry();\n  } else if (m) {\n    console.error(`Non-retryable ECCC HTTP status ${m[1]}`);\n  }\n}","preventionTips":["Poll within published rate limits and stagger requests (repo policy: stagger Yahoo-like upstreams)","Keep User-Agent and Accept headers current with API requirements","Pin and monitor the endpoint URL for upstream version changes","Distinguish retryable (429/5xx) from permanent (4xx) statuses before retrying"],"tags":["http","network","weather-api","retry"],"backgroundTag":"http-error-response","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}