Crosstalk-Solutions/project-nomad · error
${msg}
Error message
${msg} What it means
This is a catch-all error handler in ConditionsController.drugsApi that surfaces any thrown error from the external drugs API call as a 400 Bad Request with the raw error message. It is not a specific library error; whatever upstream failure occurred (network, timeout, invalid API key, malformed query) gets flattened into this response. The `message="${msg}"` form indicates the log line captures the interpolated upstream message.
Source
Thrown at admin/app/controllers/conditions_controller.ts:111
if (params.slug) {
const result = await this.service.drugsForSlug(params.slug, params.limit, filterOpts)
if (!result) {
return response.notFound({ error: 'Condition not found' })
}
return remediesOn ? result : { ...result, remedies: [] }
}
if (params.q) {
const result = await this.service.drugsForFreeText(params.q, params.limit, filterOpts)
return remediesOn ? result : { ...result, remedies: [] }
}
return response.badRequest({ error: 'Provide a slug or q query parameter' })
} catch (err) {
const msg = err instanceof Error ? err.message : String(err)
logger.warn(`[ConditionsController] drugsApi failed: ${msg}`)
return response.badRequest({ error: msg })
}
}
}
View on GitHub (pinned to 0bd1c6f4f9)
Solutions
- Check the server logs for the preceding [ConditionsController] drugsApi failed line to see the real upstream message
- Verify the request includes exactly one of slug or q query parameter
- Confirm the drugs API credentials/base URL env vars are set and valid
- If upstream is down/rate-limited, retry later or add caching/fallback for drug lookups
Example fix
// before
const res = await fetch(`/api/conditions/drugs`)
// after
const res = await fetch(`/api/conditions/drugs?q=${encodeURIComponent(query)}`)
if (!res.ok) console.error(await res.json()) Defensive patterns
Strategy: validation
Validate before calling
const params = new URLSearchParams()
if (slug) params.set('slug', slug)
else if (q?.trim()) params.set('q', q.trim())
if (![...params.keys()].length) throw new Error('slug or q required')
const res = await fetch(`/api/conditions/drugs?${params}`)
if (!res.ok) { /* surface res.error to UI, fall back to cached list */ } Type guard
const isApiError = (b: unknown): b is { error: string } =>
typeof b === 'object' && b !== null && typeof (b as any).error === 'string''
async function parseError(res: Response): Promise<string> {
const body = await res.json().catch(() => null)
return isApiError(body) ? body.error : `HTTP ${res.status}`
} Try / catch
try {
return await conditionsApi.drugs({ q })
} catch (err) {
// HTTP 400 with { error }; show message, fall back to cached results
return cachedDrugs
} Prevention
- Always send exactly one of slug or q, URL-encoded and non-empty
- Cache drug lookup results so upstream outages degrade gracefully
- Monitor for [ConditionsController] drugsApi failed warns to catch credential/upstream issues early
When it happens
Trigger: Calling GET /api/conditions/drugs without a slug or q parameter returns a deterministic 400; alternatively the upstream drugs API request fails (DNS failure, 401/403 from missing API key, timeout, or non-JSON response) and the caught error message is relayed here.
Common situations: Missing or expired DRUGS_API_KEY env var in the admin service; upstream drugs endpoint changed or is rate-limiting; developers testing locally without network access to the third-party API; frontend sending both slug and q or neither.
Related errors
AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27).
Data as JSON: /api/errors/e146859ef14fc3d1.
Report an issue: GitHub.