{"record":{"id":"27e4712e004317d2","repo":"jackwener/OpenCLI","slug":"label-returned-http-resp-status-27e471","errorCode":null,"errorMessage":"${label} returned HTTP ${resp.status}.","messagePattern":"(.+?) returned HTTP (.+?)\\.","errorType":"http","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/openfda/utils.js","lineNumber":41,"sourceCode":"    return n;\n}\n\nexport async function openfdaFetch(url, label) {\n    let resp;\n    try {\n        resp = await fetch(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });\n    } catch (err) {\n        throw new CommandExecutionError(`${label} request failed: ${err.message}`);\n    }\n    if (resp.status === 404) {\n        // openFDA returns 404 for \"no matches\" instead of an empty results array.\n        throw new EmptyResultError(label, `${label} returned 404 (no matches).`);\n    }\n    if (resp.status === 429) {\n        throw new CommandExecutionError(`${label} rate-limited (HTTP 429); back off and retry.`);\n    }\n    if (!resp.ok) {\n        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}.`);\n    }\n    let body;\n    try {\n        body = await resp.json();\n    } catch (err) {\n        throw new CommandExecutionError(`${label} returned non-JSON body: ${err.message}`);\n    }\n    return body;\n}\n\n// openFDA returns most string fields as `[string]` arrays — collapse to first\n// element. Preserves `null` (not coerced to empty string) when the slot is\n// missing entirely.\nexport function firstOrNull(arr) {\n    if (!Array.isArray(arr) || !arr.length) return null;\n    const v = arr[0];\n    if (typeof v !== 'string') return v ?? null;\n    const trimmed = v.trim();","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/openfda/utils.js#L23-L59","documentation":"For any HTTP status that is not 404 or 429 and not ok, openfdaFetch throws this CommandExecutionError reporting the raw status code. It is the catch-all for server-side or authorization problems at api.fda.gov that are neither 'no matches' nor throttling. The label names the originating command.","triggerScenarios":"HTTP 500/502/503 when openFDA is down or a backend error occurs; HTTP 401/403 when an invalid API key is supplied or traffic is blocked; HTTP 400 from a malformed query that slips past client validation.","commonSituations":"openFDA maintenance windows returning 503; an expired or mistyped API key yielding 403; an over-complex search string the API rejects with 400; a load balancer 502 during traffic spikes.","solutions":["Read the status: 5xx means retry later with backoff; 401/403 means fix the API key; 400 means fix the query syntax.","Check the openFDA status page / open.fda.gov to see if the API is degraded.","Validate and simplify the search query (fewer clauses, proper +AND+ / +OR+ encoding).","Include any configured API key correctly in the request and confirm it is still valid."],"exampleFix":"// before\nawait openfdaFetch(`${OPENFDA_BASE}/drug/label.json?search=${rawQuery}`, 'openfda drug-label'); // 500 risk\n// after\ntry {\n  await openfdaFetch(`${OPENFDA_BASE}/drug/label.json?search=${encodeURIComponent(rawQuery)}`, 'openfda drug-label');\n} catch (e) {\n  await new Promise(r => setTimeout(r, 5000)); // retry on transient 5xx\n  return openfdaFetch(url, 'openfda drug-label');\n}","handlingStrategy":"try-catch","validationCode":"function buildSearchQuery(clauses) {\n  if (!Array.isArray(clauses) || clauses.length === 0) throw new Error('at least one search clause required');\n  return clauses.map(encodeURIComponent).join('+AND+');\n}","typeGuard":"function isServerError(e) {\n  const m = e?.message?.match(/returned HTTP (\\d{3})\\./);\n  return m != null && Number(m[1]) >= 500;\n}","tryCatchPattern":"try {\n  const body = await openfdaFetch(url, label);\n} catch (e) {\n  const m = e.message.match(/returned HTTP (\\d{3})\\./);\n  const code = m && Number(m[1]);\n  if (code >= 500) { /* backoff + retry */ }\n  else if (code === 401 || code === 403) { /* fix API key */ }\n  else if (code === 400) { /* fix query syntax */ }\n  else throw e;\n}","preventionTips":["Retry 5xx with exponential backoff; they are usually transient.","Validate API keys before deployment; 401/403 means bad key.","Keep queries simple and correctly encoded to avoid 400s.","Monitor the openFDA status page for degradation windows."],"tags":["http","api","openfda","server-error"],"backgroundTag":"http-error-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}