{"record":{"id":"ea639e42dfda493d","repo":"koala73/worldmonitor","slug":"invalid-variant-ea639e","errorCode":"invalid_variant","errorMessage":"variant must be one of: full, tech, finance, commodity, energy, happy.","messagePattern":"variant must be one of: full, tech, finance, commodity, energy, happy\\.","errorType":"error_code","errorClass":"DashboardPanelCatalogError","httpStatus":null,"severity":"error","filePath":"src/services/webmcp-panel-catalog.ts","lineNumber":142,"sourceCode":"\nexport function getDashboardPanelCategoryKey(panelId: string, variant: string): string {\n  for (const [key, definition] of Object.entries(PANEL_CATEGORY_MAP)) {\n    if (definition.variants && !definition.variants.includes(variant)) continue;\n    if (definition.panelKeys.includes(panelId)) return key;\n  }\n  for (const [key, definition] of Object.entries(PANEL_CATEGORY_MAP)) {\n    if (definition.panelKeys.includes(panelId)) return key;\n  }\n  return 'other';\n}\n\nexport function listDashboardPanelCatalog(\n  live: DashboardPanelCatalogLiveState,\n  query: DashboardPanelCatalogQuery = {},\n): DashboardPanelCatalogPage {\n  const currentVariant = live.currentVariant;\n  if (!isSiteVariant(currentVariant)) {\n    throw new DashboardPanelCatalogError(\n      'invalid_variant',\n      'variant must be one of: full, tech, finance, commodity, energy, happy.',\n    );\n  }\n\n  const variantFilter = query.variant;\n  if (variantFilter !== undefined) {\n    if (typeof variantFilter !== 'string' || !isSiteVariant(variantFilter)) {\n      throw new DashboardPanelCatalogError(\n        'invalid_variant',\n        'variant must be one of: full, tech, finance, commodity, energy, happy.',\n      );\n    }\n  }\n\n  const categoryFilter = query.category;\n  if (categoryFilter !== undefined) {\n    if (","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/src/services/webmcp-panel-catalog.ts#L124-L160","documentation":"listDashboardPanelCatalog() validates live.currentVariant with isSiteVariant() before enumerating panels. An unrecognized current variant makes variant-aware panel lookups (VARIANT_DEFAULTS, availability sets) impossible, so it throws DashboardPanelCatalogError with reason 'invalid_variant'. Acceptable values are full, tech, finance, commodity, energy, happy.","triggerScenarios":"Supplying a DashboardPanelCatalogLiveState whose currentVariant is not exactly one of the six SITE_VARIANTS — e.g. 'main', 'default', 'FULL', an empty string, or a stale value persisted before a variant rename — when listDashboardPanelCatalog runs (via listWebMcpDashboardPanels).","commonSituations":"currentVariant sourced from config, localStorage, or a hostname→variant mapping that drifted from SITE_VARIANTS; bootstrapping the dashboard before the variant is resolved; case mismatch after lowercasing was removed.","solutions":["Ensure live.currentVariant is one of 'full','tech','finance','commodity','energy','happy'; consult @/config/variant for the source of truth.","Normalize and validate with isSiteVariant() when constructing the live state; fall back to the default variant if invalid.","Map legacy/alias variant names to current ids before the call.","Catch DashboardPanelCatalogError with reason 'invalid_variant' to report the accepted variants back to the caller."],"exampleFix":"// before\nlistDashboardPanelCatalog({ currentVariant: appConfig.variant, ... });\n// after\nconst variant = isSiteVariant(appConfig.variant) ? appConfig.variant : VARIANT_DEFAULTS.defaultVariant;\nlistDashboardPanelCatalog({ currentVariant: variant, ... });","handlingStrategy":"validation","validationCode":"import { isSiteVariant } from '@/config/variant';\nif (!isSiteVariant(live.currentVariant)) {\n  throw new Error(`currentVariant must be one of full|tech|finance|commodity|energy|happy, got '${live.currentVariant}'`);\n}","typeGuard":"function hasValidCurrentVariant(live: DashboardPanelCatalogLiveState): live is DashboardPanelCatalogLiveState & { currentVariant: SiteVariant } {\n  return isSiteVariant(live.currentVariant);\n}","tryCatchPattern":"try {\n  return listDashboardPanelCatalog(live, query);\n} catch (err) {\n  if (err instanceof DashboardPanelCatalogError && err.reason === 'invalid_variant') {\n    return listDashboardPanelCatalog({ ...live, currentVariant: DEFAULT_VARIANT }, query);\n  }\n  throw err;\n}","preventionTips":["Resolve currentVariant through isSiteVariant() at dashboard bootstrap, before any catalog call.","Keep hostname/config→variant mappings in sync with SITE_VARIANTS.","Fall back to a known-good default variant when the resolved value is unrecognized.","Write a unit test per variant to catch registry drift early."],"tags":["validation","variant","panel-catalog","dashboard"],"backgroundTag":"invalid-enum-value","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}