{"record":{"id":"9af54d8d1f3eab4c","repo":"koala73/worldmonitor","slug":"tool-failure-messages-name","errorCode":null,"errorMessage":"${TOOL_FAILURE_MESSAGES[name]}","messagePattern":"\\$\\{TOOL_FAILURE_MESSAGES\\[name\\]\\}","errorType":"error_code","errorClass":"SafeWebMcpError","httpStatus":null,"severity":"error","filePath":"src/services/webmcp.ts","lineNumber":796,"sourceCode":"  ) => Promise<unknown> | unknown,\n  trackEvent: WebMcpAnalytics,\n  hooks: WebMcpInvocationHooks = {},\n): DashboardWebMcpTool['execute'] {\n  return async (args, extra?: WebMcpToolExecutionContext) => {\n    const signal = isAbortSignal(extra?.signal) ? extra.signal : undefined;\n    const execution = signal ? { signal } : undefined;\n    markLcpDebug('wm:webmcp:tool-start', {\n      tool: name,\n      targetCancellationSupported: Boolean(signal),\n    });\n    try {\n      throwIfWebMcpAborted(signal);\n      const preflightResult = await hooks.preflight?.(args, execution);\n      throwIfWebMcpAborted(signal);\n      let result: unknown;\n      if (preflightResult !== undefined) {\n        result = preflightResult;\n      } else if (CANCELLATION_REQUIRED_WEBMCP_TOOLS.has(name) && !signal) {\n        // This tool declared that a phantom completion would be unsafe, and\n        // the host cannot deliver the target-side signal. Return a structured\n        // denial because some hosts erase the name and message of errors\n        // raised by the page callback.\n        result = unsupportedCancellationResult();\n      } else {\n        result = await fn(args, execution);\n        // Browser cancellation rejects executeTool independently of this\n        // callback. Re-check here so late work cannot publish success telemetry\n        // after the host has already cancelled the invocation.\n        throwIfWebMcpAborted(signal);\n      }\n      enforceOutputBudget(result);\n      const invocation = classifyInvocationResult(result);\n      reportWebMcpEvent(trackEvent, 'webmcp-tool-invoked', {\n        tool: name,\n        ...invocation,\n        ...(hooks.successMetadata?.(args, result) ?? {}),","sourceCodeStart":778,"sourceCodeEnd":814,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/src/services/webmcp.ts#L778-L814","documentation":"The final fallback in withInvocationLogging: any error thrown by a tool that is not a SafeWebMcpError, DashboardUnavailableError, DashboardPanelCatalogError, or MissionPresetCatalogError is replaced with a generic per-tool message from TOOL_FAILURE_MESSAGES (e.g. 'World Monitor could not switch monitors.'). The original error details are deliberately suppressed so raw internals never leak to the MCP client; analytics still record the true reason.","triggerScenarios":"Any unexpected exception inside a tool handler — a null dereference while reading dashboard state, an unhandled DOM/map error, a bug in applyDashboardAction, or any thrown non-catalog error not classified by the preceding instanceof checks.","commonSituations":"Dashboard DOM not in the expected state when a tool mutates it; race between UI updates and tool execution; bugs introduced by dashboard updates breaking a tool; client calling a tool while the map or panels are in a transient state.","solutions":["Check browser devtools console / WebMCP analytics logs — the generic message hides the real error, which is logged there (classifyInvocationError / trackEvent).","Re-run the tool after the dashboard is idle (no in-flight animations or pending mutations).","Reproduce with the same arguments and file a bug with the tool name and args; a fallback message means an unhandled internal error.","Upgrade WorldMonitor — many internal failures are fixed in later dashboard releases."],"exampleFix":"// before\nawait tools.call('move_panel', { id: 'x', direction: 'left' }); // generic failure, no detail\n// after\ntry {\n  await tools.call('move_panel', { id: 'x', direction: 'left' });\n} catch (e) {\n  console.error('move_panel failed:', e.message); // inspect console + analytics for real cause\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isGenericToolFailure(e) {\n  return e instanceof Error && /^World Monitor could not /.test(e.message);\n}","tryCatchPattern":"try {\n  return await tools.call(name, args);\n} catch (e) {\n  if (isGenericToolFailure(e)) {\n    console.error(`tool ${name} failed internally with args`, args, e.message);\n    // check analytics/devtools console for the suppressed root cause\n  }\n  throw e;\n}","preventionTips":["Treat this generic message as a signal to inspect console/analytics, since the root cause is hidden.","Avoid calling mutating tools while the dashboard is mid-animation or mid-update.","Keep WorldMonitor and your integration on matching versions.","Capture tool name + args in your own logging to aid bug reports."],"tags":["webmcp","internal-error","fallback","unhandled-exception"],"backgroundTag":"unhandled-tool-exception","analyzedSha":"e586b8b4b80f595aa7ece295eec10d76f2921240","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}