koala73/worldmonitor · error · SafeWebMcpError

Dashboard tab list exceeded the safe output limit.

Error message

Dashboard tab list exceeded the safe output limit.

What it means

The dashboard tab list bounder builds a page of tabs with a nextCursor and validates the serialized page against MAX_OUTPUT_CHARS. If a single page of the tab list still cannot fit within the safe output budget, it throws this SafeWebMcpError instead of returning an oversized result. It guarantees list_dashboard_tabs responses stay within the WebMCP channel limits.

Solutions

  1. Rename or clean up tabs with excessively long names; trim persisted tab state.
  2. Use cursor-based pagination and request the list in multiple calls rather than one large page.
  3. Reset the tab store if it has grown pathologically (export needed tabs, clear, recreate).
  4. If a single tab alone cannot fit the budget, report a bug — the bounder should truncate per-tab fields.

Example fix

// before
const { tabs } = await tools.call('list_dashboard_tabs', { limit: 1000 });
// after
const { tabs } = await tools.call('list_dashboard_tabs', { limit: 20, cursor });
// and keep tab names short when calling create/rename_dashboard_tab
Defensive patterns

Strategy: validation

Validate before calling

function validTabListArgs(args) {
  return (args.limit === undefined || (Number.isInteger(args.limit) && args.limit > 0))
    && (args.cursor === undefined || typeof args.cursor === 'string');
}

Try / catch

try {
  return await tools.call('list_dashboard_tabs', { limit: 20, cursor });
} catch (e) {
  if (e.message.includes('tab list exceeded')) {
    return tools.call('list_dashboard_tabs', { limit: 1, cursor });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling list_dashboard_tabs when even one page of tabs (including the forced first-entry fallback) serializes beyond MAX_OUTPUT_CHARS — e.g. tabs with extremely long names/urls/state blobs.

Common situations: Users with many tabs where each tab object carries verbose metadata; tabs with very long user-chosen names or embedded state; a corrupted/overgrown persisted tab store.

Related errors


AI-assisted analysis of koala73/worldmonitor@e586b8b4b8 (2026-09-01). Data as JSON: /api/errors/183777c264db3727. Report an issue: GitHub.

Appendix: source

Thrown at src/services/webmcp.ts:1536

    ? result.actionType
    : 'move';
  const status = result.status === 'applied' || result.status === 'denied' || result.status === 'invalid'
    ? result.status
    : 'denied';
  const ok = result.ok === true && status === 'applied';
  const reason = result.reason && PANEL_LAYOUT_DENIAL_REASON_SET.has(result.reason)
    ? result.reason
    : undefined;
  const region = result.region === 'sidebar' || result.region === 'bottom'
    ? result.region
    : undefined;
  return {
    ok,
    status: ok ? 'applied' : status === 'invalid' ? 'invalid' : 'denied',
    actionType,
    ...(result.panelId !== undefined
      ? { panelId: boundedText(result.panelId, PANEL_LAYOUT_ID_MAX_CHARS) }
      : {}),
    ...(region ? { region } : {}),
    ...(typeof result.index === 'number' ? { index: Math.max(0, Math.floor(result.index)) } : {}),
    ...(typeof result.requestedCollapsed === 'boolean'
      ? { requestedCollapsed: result.requestedCollapsed }
      : {}),
    ...(typeof result.effectiveCollapsed === 'boolean'
      ? { effectiveCollapsed: result.effectiveCollapsed }
      : {}),
    ...(typeof result.requestedFullscreen === 'boolean'
      ? { requestedFullscreen: result.requestedFullscreen }
      : {}),
    ...(typeof result.effectiveFullscreen === 'boolean'
      ? { effectiveFullscreen: result.effectiveFullscreen }
      : {}),
    ...(ok ? { changed: result.changed === true } : { changed: false }),
    ...(result.unchanged === true ? { unchanged: true } : {}),
    ...(typeof result.persisted === 'boolean' ? { persisted: result.persisted } : {}),
    ...(!ok && reason ? { reason } : {}),

View on GitHub (pinned to e586b8b4b8)