{"record":{"id":"f477a44188b34988","repo":"koala73/worldmonitor","slug":"unsupported-fred-series-id","errorCode":null,"errorMessage":"Unsupported FRED series ID","messagePattern":"Unsupported FRED series ID","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"warning","filePath":"server/worldmonitor/economic/v1/get-fred-series.ts","lineNumber":24,"sourceCode":"import type {\n  ServerContext,\n  GetFredSeriesRequest,\n  GetFredSeriesResponse,\n} from '../../../../src/generated/server/worldmonitor/economic/v1/service_server';\nimport { ValidationError } from '../../../../src/generated/server/worldmonitor/economic/v1/service_server';\n\nimport { getCachedJson } from '../../../_shared/redis';\nimport { markNoStoreFallbackResponse, setResponseHeader } from '../../../_shared/response-headers';\nimport { ALLOWED_FRED_SERIES, applyFredObservationLimit, fredSeedKey, normalizeFredLimit } from './_fred-shared';\n\nexport async function getFredSeries(\n  ctx: ServerContext,\n  req: GetFredSeriesRequest,\n): Promise<GetFredSeriesResponse> {\n  const seriesId = (req.seriesId ?? '').trim().toUpperCase();\n  if (!ALLOWED_FRED_SERIES.has(seriesId)) {\n    setResponseHeader(ctx.request, 'Cache-Control', 'no-store');\n    throw new ValidationError([{ field: 'series_id', description: 'Unsupported FRED series ID' }]);\n  }\n  try {\n    const seedKey = fredSeedKey(seriesId);\n    const result = await getCachedJson(seedKey, true) as GetFredSeriesResponse | null;\n    if (!result?.series) return markNoStoreFallbackResponse(ctx.request, { series: undefined });\n    const limit = normalizeFredLimit(req.limit);\n    return { series: applyFredObservationLimit(result.series, limit) };\n  } catch {\n    return markNoStoreFallbackResponse(ctx.request, { series: undefined });\n  }\n}\n","sourceCodeStart":6,"sourceCodeEnd":36,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/server/worldmonitor/economic/v1/get-fred-series.ts#L6-L36","documentation":"getFredSeries only serves an allowlist of FRED series IDs (ALLOWED_FRED_SERIES). If the normalized req.seriesId is not in that set, the handler marks the response no-store and throws a ValidationError on field 'series_id' with 'Unsupported FRED series ID'. This prevents arbitrary upstream FRED fetches.","triggerScenarios":"Calling get-fred-series with seriesId 'GDP2', 'MY_CUSTOM_SERIES', a lowercase-but-unknown id, or any real FRED series not yet added to the allowlist.","commonSituations":"Assuming any valid FRED mnemonic works; typos in the series ID; a series removed from or never added to the allowlist after a FRED catalog change; user-entered series in a config UI.","solutions":["Pick a seriesId from the supported allowlist (check ALLOWED_FRED_SERIES in server/worldmonitor/economic/v1/get-fred-series.ts)","Trim and uppercase your seriesId before calling to match normalization","If you need a new series, add it to ALLOWED_FRED_SERIES in the server code rather than working around it"],"exampleFix":"// before\nawait getFredSeries(ctx, { seriesId: 'CIVPART2' });\n// after\nawait getFredSeries(ctx, { seriesId: 'CIVPART' }); // ID present in ALLOWED_FRED_SERIES","handlingStrategy":"validation","validationCode":"const id = (seriesId ?? '').trim().toUpperCase();\nif (!ALLOWED_FRED_SERIES.has(id)) throw new Error(`seriesId ${id} is not in the supported allowlist`);","typeGuard":null,"tryCatchPattern":"try {\n  return await getFredSeries(ctx, { seriesId });\n} catch (e) {\n  if (e instanceof ValidationError && e.issues.some(i => i.field === 'series_id')) {\n    return { error: 'unsupported series', supported: [...ALLOWED_FRED_SERIES] };\n  }\n  throw e;\n}","preventionTips":["Keep a client-side copy of the allowlist to validate inputs","Uppercase/trim series IDs to match server normalization","When onboarding new series, update ALLOWED_FRED_SERIES server-side first"],"tags":["validation","allowlist","economic"],"backgroundTag":"invalid-enum-value","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"}