koala73/worldmonitor · error · DashboardBindingError
app_destroyed
app_destroyed
Error message
Dashboard is no longer available.
What it means
DashboardBindingError 'app_destroyed' thrown by getWebMcpDashboardContext() when ctx.isDestroyed is true. The context snapshot builder must never read UI state from a destroyed app, so it refuses before touching ctx.panels or ctx.map. This is the snapshot-getter sibling of the tool-binding guards in App.ts.
Source
Thrown at src/app/webmcp-dashboard.ts:40
...result,
ok: false,
status: 'denied',
reason,
message: reason === 'viewport_superseded'
? 'Map movement was superseded by a newer viewport action.'
: reason === 'renderer_changed'
? 'Map renderer changed before the movement completed.'
: 'Map movement was interrupted before it completed.',
targets: result.targets.map((target) => ({ ...target, status: 'denied', reason })),
};
}
export function getWebMcpDashboardContext(
ctx: AppContext,
variant: string,
): DashboardContextSnapshot {
if (ctx.isDestroyed) {
throw new DashboardBindingError('app_destroyed', 'Dashboard is no longer available.');
}
if (!ctx.map) {
throw new DashboardBindingError('map_unavailable', 'Map is not available.');
}
const mapState = ctx.map.getState();
const center = ctx.map.getCenter();
return {
variant,
map: {
view: mapState.view,
center,
zoom: mapState.zoom,
timeRange: mapState.timeRange,
enabledLayers: Object.entries(mapState.layers)
.filter(([, enabled]) => enabled === true)
.map(([layer]) => layer),View on GitHub (pinned to eeab0a219f)
Solutions
- Check ctx.isDestroyed before requesting a context snapshot and skip/queue the call
- Catch DashboardBindingError, branch on code 'app_destroyed', and drop or re-derive the tool bindings against a new App instance
- Unregister WebMCP tools in destroy() (the controller stored from registerWebMcpTools exists for exactly this) so agents cannot reach a dead context
Example fix
// before
const snapshot = getWebMcpDashboardContext(ctx, variant); // throws on destroyed ctx
// after
if (ctx.isDestroyed) {
return { ok: false, status: 'denied', reason: 'app_destroyed' } as const;
}
const snapshot = getWebMcpDashboardContext(ctx, variant); Defensive patterns
Strategy: type-guard
Validate before calling
function isContextUsable(ctx: AppContext): boolean {
return !ctx.isDestroyed;
}
if (!isContextUsable(ctx)) return { ok: false, status: 'denied', reason: 'app_destroyed' }; Type guard
function hasLiveAppContext(ctx: AppContext): boolean { return !ctx.isDestroyed; } Try / catch
try { return getWebMcpDashboardContext(ctx, variant); } catch (e) { if (e instanceof DashboardBindingError && e.code === 'app_destroyed') return DESTROYED_SNAPSHOT; throw e; } Prevention
- Check ctx.isDestroyed before reading any snapshot state
- Unregister WebMCP tools in destroy() via the stored controller so agents cannot reach a dead app
- Version snapshots by app instance so stale ones are dropped
When it happens
Trigger: An agent calls get_dashboard_context after the App was destroyed — e.g. the tool call raced page unload, HMR teardown, or explicit app.destroy(). The first thing the function does is refuse dead contexts; note the very next guard throws 'map_unavailable' for a live app without a map, so hitting this message specifically means the app itself is gone.
Common situations: Agent sessions that outlive the dashboard (client keeps calling tools after the tab started unloading); tests exercising context snapshots against a destroyed fixture; stale tool registrations surviving an HMR boundary.
Related errors
- app_destroyed
- Dashboard is no longer available.
- Search manager destroyed
- Country brief panel is not initialised
- map_unavailable
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/db3de5d0aa221b3e.
Report an issue: GitHub.