{"record":{"id":"a0f1c5fa168d2270","repo":"koala73/worldmonitor","slug":"validation","errorCode":"validation","errorMessage":"${error.message}","messagePattern":"\\$\\{error\\.message\\}","errorType":"error_code","errorClass":"SafeWebMcpError","httpStatus":null,"severity":"error","filePath":"src/services/webmcp.ts","lineNumber":786,"sourceCode":"      reportWebMcpEvent(trackEvent, 'webmcp-tool-invoked', {\n        tool: name,\n        outcome: 'failure',\n        reason,\n      });\n      if (signal?.aborted) throwIfWebMcpAborted(signal);\n      if (error instanceof SafeWebMcpError) throw error;\n      if (isWebMcpAbortError(error)) throw error;\n      if (error instanceof DashboardBindingError) {\n        throw new SafeWebMcpError(\n          `Dashboard unavailable: ${boundedText(error.message, 160)} Reason: ${error.reason}.`,\n          'unavailable',\n        );\n      }\n      if (error instanceof DashboardPanelCatalogError) {\n        throw new SafeWebMcpError(error.message, 'validation');\n      }\n      if (error instanceof MissionPresetCatalogError) {\n        throw new SafeWebMcpError(error.message, 'validation');\n      }\n      throw new SafeWebMcpError(TOOL_FAILURE_MESSAGES[name]);\n    }\n  };\n}\n\nfunction enforceOutputBudget(value: unknown): void {\n  const serialized = JSON.stringify(value);\n  if (typeof serialized !== 'string' || serialized.length > MAX_OUTPUT_CHARS) {\n    throw new SafeWebMcpError('Tool output exceeded the safe output limit.');\n  }\n}\n\nfunction structuredResultReasons(result: Record<string, unknown>): string[] {\n  const reasons = typeof result.reason === 'string' ? [result.reason] : [];\n  if (!Array.isArray(result.targets)) return reasons;\n  for (const target of result.targets) {\n    if (target && typeof target === 'object' && 'reason' in target) {","sourceCodeStart":768,"sourceCodeEnd":804,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/src/services/webmcp.ts#L768-L804","documentation":"withInvocationLogging maps DashboardPanelCatalogError and MissionPresetCatalogError to a SafeWebMcpError with code 'validation', reusing the catalog error's own message verbatim. These catalog errors mean the requested panel/mission-preset catalog query or identifier was rejected — the arguments failed catalog-level validation before any dashboard action ran.","triggerScenarios":"list_dashboard_panels with an invalid variant/category filter value; open_dashboard_panel / set_panel_enabled with an unknown panel id; list_mission_presets / apply_mission_preset with an unknown preset id or malformed query — i.e. any call that makes the panel or mission-preset catalog throw.","commonSituations":"Hard-coded panel ids that were renamed or removed in a newer dashboard version; category/variant strings that don't match the catalog enum; stale tool integrations written against an older panel catalog; typos in preset names.","solutions":["Use list_dashboard_panels / list_mission_presets first and pass ids/values straight from that response instead of hard-coding them.","Match the variant/category against the catalog's accepted enum values exactly (case-sensitive).","Refresh your integration against the current dashboard version if a previously working id stopped resolving.","Catch this error and surface the embedded message — it already describes the exact catalog validation problem."],"exampleFix":"// before\nawait tools.call('open_dashboard_panel', { id: 'panel-cpu-temp' });\n// after\nconst { panels } = await tools.call('list_dashboard_panels', {});\nconst panel = panels.find((p) => p.id === 'panel-cpu-temp');\nif (!panel) throw new Error('panel-cpu-temp no longer exists in catalog');\nawait tools.call('open_dashboard_panel', { id: panel.id });","handlingStrategy":"validation","validationCode":"async function panelExists(tools, id) {\n  const { panels } = await tools.call('list_dashboard_panels', {});\n  return panels.some((p) => p.id === id);\n}","typeGuard":"function isCatalogValidationError(e) {\n  return e instanceof Error && e.name === 'WebMcpToolError' && typeof e.message === 'string';\n}","tryCatchPattern":"try {\n  await tools.call('open_dashboard_panel', { id });\n} catch (e) {\n  if (/catalog|panel/i.test(e.message)) {\n    const { panels } = await tools.call('list_dashboard_panels', {});\n    const match = panels.find((p) => p.id === id);\n    if (match) return tools.call('open_dashboard_panel', { id: match.id });\n  }\n  throw e;\n}","preventionTips":["Never hard-code panel or preset ids; resolve them via the list tools at runtime.","Keep a cached catalog and invalidate it on dashboard version changes.","Match enum values (variant/category) exactly and case-sensitively.","Re-verify ids after any WorldMonitor upgrade."],"tags":["webmcp","validation","catalog","invalid-arguments"],"backgroundTag":"catalog-validation-failed","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"}