{"record":{"id":"b01dd361c162354e","repo":"koala73/worldmonitor","slug":"dashboard-navigation-result-exceeded-the-safe-outp","errorCode":null,"errorMessage":"Dashboard navigation result exceeded the safe output limit.","messagePattern":"Dashboard navigation result exceeded the safe output limit\\.","errorType":"error_code","errorClass":"SafeWebMcpError","httpStatus":null,"severity":"error","filePath":"src/services/webmcp.ts","lineNumber":1603,"sourceCode":"    ok: result.ok === true,\n    status: result.status,\n    ...(result.destination ? { destination: boundedText(result.destination, 32) } : {}),\n    ...(result.navigation ? { navigation: boundedText(result.navigation, 16) } : {}),\n    ...(result.overlay ? { overlay: boundedText(result.overlay, 16) } : {}),\n    ...(result.tab ? { tab: boundedText(result.tab, 32) } : {}),\n    ...(result.reason ? { reason: boundedText(result.reason, 64) } : {}),\n    message: boundedText(result.message, 240),\n    context: {},\n  };\n  const envelopeChars = JSON.stringify(envelope).length;\n  // `\"context\":{}` is already in the envelope; the empty object is 2 chars.\n  const contextBudget = Math.max(0, NAVIGATION_MAX_OUTPUT_CHARS - envelopeChars + 2);\n  const bounded = {\n    ...envelope,\n    context: boundDashboardContext(result.context ?? EMPTY_NAV_CONTEXT, contextBudget),\n  };\n  if (JSON.stringify(bounded).length > NAVIGATION_MAX_OUTPUT_CHARS) {\n    throw new SafeWebMcpError('Dashboard navigation result exceeded the safe output limit.');\n  }\n  return bounded;\n}\n\nasync function applyDashboardTabAction(\n  action: DashboardTabAction,\n  app: WebMcpAppBindings,\n  options?: WebMcpExecutionOptions,\n): Promise<DashboardTabActionResult> {\n  const result = await app.applyDashboardTabAction(action, options);\n  return isDashboardTabListSnapshot(result)\n    ? boundDashboardTabList(result, action.type === 'list' ? action.cursor : undefined)\n    : boundDashboardTabMutation(result);\n}\n\nfunction hasOnlyOwnKeys(\n  value: Record<string, unknown>,\n  allowedKeys: readonly string[],","sourceCodeStart":1585,"sourceCodeEnd":1621,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/src/services/webmcp.ts#L1585-L1621","documentation":"boundDashboardNavigationResult composes a navigation envelope plus a context snapshot whose size is pre-budgeted (contextBudget = NAVIGATION_MAX_OUTPUT_CHARS - envelopeChars + 2), then does a final check against NAVIGATION_MAX_OUTPUT_CHARS. If the final serialized navigation result (e.g. from switch_monitor or focus_country) still exceeds the navigation output budget it throws this SafeWebMcpError. It is the last-resort guard for navigation payloads.","triggerScenarios":"A navigation tool call (switch_monitor, focus_country, applyDashboardAction-backed tools) where the envelope itself is so large that even after shrinking the context to zero the total exceeds NAVIGATION_MAX_OUTPUT_CHARS.","commonSituations":"A dashboard version adding large fields to the navigation envelope; monitors/countries with pathological metadata inflating envelope fields; version mismatch between tool bounder and app payload shape.","solutions":["Retry the navigation — if a transient field inflated the envelope it may pass on a second call.","Update WorldMonitor so tool bounder and dashboard payload shapes are from the same version.","Reduce navigation input surface (e.g. target a monitor/country without huge metadata) to confirm the trigger.","Report a bug with the tool name — the envelope should be structurally bounded, not able to exceed the budget."],"exampleFix":"// before\nawait tools.call('switch_monitor', { monitor: someUnvalidatedInput });\n// after\nconst monitors = await tools.call('get_dashboard_context', {});\nconst valid = monitors.monitors.some((m) => m.id === target);\nif (valid) await tools.call('switch_monitor', { monitor: target });","handlingStrategy":"try-catch","validationCode":"async function monitorExists(tools, monitor) {\n  const ctx = await tools.call('get_dashboard_context', {});\n  return Array.isArray(ctx.monitors) && ctx.monitors.some((m) => m.id === monitor);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await tools.call('switch_monitor', { monitor });\n} catch (e) {\n  if (e.message.includes('navigation result exceeded')) {\n    console.error('oversized navigation result for', monitor);\n    // fall back to a context refresh instead of the full navigation payload\n    return tools.call('get_dashboard_context', {});\n  }\n  throw e;\n}","preventionTips":["Validate target monitor/country ids against get_dashboard_context before navigating.","Keep tool and app versions aligned so envelope shapes stay bounded.","Avoid navigating to targets known to carry huge metadata until trimmed.","Treat repeated occurrences as a regression to report upstream."],"tags":["webmcp","output-budget","navigation","context-snapshot"],"backgroundTag":"response-size-limit-exceeded","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"}