koala73/worldmonitor · error · SafeWebMcpError

${TOOL_FAILURE_MESSAGES[name]}

Error message

${TOOL_FAILURE_MESSAGES[name]}

What it means

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.

Source

Thrown at src/services/webmcp.ts:788

        outcome: 'failure',
        reason,
      });
      if (signal?.aborted) throwIfWebMcpAborted(signal);
      if (error instanceof SafeWebMcpError) throw error;
      if (isWebMcpAbortError(error)) throw error;
      if (error instanceof DashboardBindingError) {
        throw new SafeWebMcpError(
          `Dashboard unavailable: ${boundedText(error.message, 160)} Reason: ${error.reason}.`,
          'unavailable',
        );
      }
      if (error instanceof DashboardPanelCatalogError) {
        throw new SafeWebMcpError(error.message, 'validation');
      }
      if (error instanceof MissionPresetCatalogError) {
        throw new SafeWebMcpError(error.message, 'validation');
      }
      throw new SafeWebMcpError(TOOL_FAILURE_MESSAGES[name]);
    }
  };
}

function enforceOutputBudget(value: unknown): void {
  const serialized = JSON.stringify(value);
  if (typeof serialized !== 'string' || serialized.length > MAX_OUTPUT_CHARS) {
    throw new SafeWebMcpError('Tool output exceeded the safe output limit.');
  }
}

function structuredResultReasons(result: Record<string, unknown>): string[] {
  const reasons = typeof result.reason === 'string' ? [result.reason] : [];
  if (!Array.isArray(result.targets)) return reasons;
  for (const target of result.targets) {
    if (target && typeof target === 'object' && 'reason' in target) {
      const reason = (target as { reason?: unknown }).reason;
      if (typeof reason === 'string') reasons.push(reason);

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Check browser devtools console / WebMCP analytics logs — the generic message hides the real error, which is logged there (classifyInvocationError / trackEvent).
  2. Re-run the tool after the dashboard is idle (no in-flight animations or pending mutations).
  3. Reproduce with the same arguments and file a bug with the tool name and args; a fallback message means an unhandled internal error.
  4. Upgrade WorldMonitor — many internal failures are fixed in later dashboard releases.

Example fix

// before
await tools.call('move_panel', { id: 'x', direction: 'left' }); // generic failure, no detail
// after
try {
  await tools.call('move_panel', { id: 'x', direction: 'left' });
} catch (e) {
  console.error('move_panel failed:', e.message); // inspect console + analytics for real cause
}
Defensive patterns

Strategy: try-catch

Type guard

function isGenericToolFailure(e) {
  return e instanceof Error && /^World Monitor could not /.test(e.message);
}

Try / catch

try {
  return await tools.call(name, args);
} catch (e) {
  if (isGenericToolFailure(e)) {
    console.error(`tool ${name} failed internally with args`, args, e.message);
    // check analytics/devtools console for the suppressed root cause
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-09-01). Data as JSON: /api/errors/9af54d8d1f3eab4c. Report an issue: GitHub.