{"record":{"id":"c0773dda54b53efd","repo":"koala73/worldmonitor","slug":"search-modal-is-not-initialised","errorCode":null,"errorMessage":"Search modal is not initialised","messagePattern":"Search modal is not initialised","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/App.ts","lineNumber":1816,"sourceCode":"      }\n\n      const togglingBeforeLoad = Boolean(options.toggle) && !this.searchManager;\n      if (togglingBeforeLoad) {\n        this.searchToggleDesiredOpen = !this.searchToggleDesiredOpen;\n      }\n\n      epoch = ++this.openSearchEpoch;\n      const manager = await raceWebMcpAbort(this.ensureSearchManager(), options.signal);\n      throwIfWebMcpAborted(options.signal);\n      if (this.openSearchEpoch !== epoch) return false;\n      if (pendingGate && !pendingGate.isCurrent()) return false;\n\n      const wantOpen = togglingBeforeLoad ? this.searchToggleDesiredOpen : true;\n      if (!wantOpen) return false;\n\n      manager.updateSearchIndex();\n      const modal = this.state.searchModal;\n      if (!modal) throw new Error('Search modal is not initialised');\n      throwIfWebMcpAborted(options.signal);\n      modal.open(pendingGate ? pendingId : options.replaceOverlayId);\n      if (options.initialQuery) modal.applyQuery(options.initialQuery);\n      return modal.isOpen();\n    } catch (error) {\n      const actionWasCancelled = pendingGate !== null && !pendingGate.isCurrent();\n      const invocationWasCancelled = options.signal?.aborted === true;\n      if (!this.state.isDestroyed && !actionWasCancelled && !invocationWasCancelled) {\n        console.warn('[search] Failed to load search manager:', error);\n        if (!options.throwOnFailure) showToast('Search failed to load. Please try again.');\n      }\n      pendingGate?.cancel();\n      if (options.throwOnFailure || options.signal?.aborted) throw error;\n      return false;\n    } finally {\n      // Reset the toggle accumulator once the latest press settles.\n      if (this.openSearchEpoch === epoch) this.searchToggleDesiredOpen = false;\n    }","sourceCodeStart":1798,"sourceCodeEnd":1834,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/src/App.ts#L1798-L1834","documentation":"App's search-open path reads this.state.searchModal; if it is still null (search modal never initialized during boot, or torn down), it throws 'Search modal is not initialised'. This is an internal lifecycle guard ensuring modal.open() is only called on a live modal instance.","triggerScenarios":"Triggering search (keyboard shortcut or programmatic openSearch) before the app finishes initializing the search modal, or after the modal was destroyed; toggling search during boot or teardown races.","commonSituations":"User hits the search hotkey very early during page load; automated tests/e2e driving search before init completes; rapid destroy/re-init cycles (e.g. view switches) leaving state.searchModal null.","solutions":["Wait for app boot/init completion before invoking search programmatically","Check app state for searchModal availability before opening","Ensure the search modal initializer runs before registering the search hotkey","If caused by a teardown race, guard re-open calls against the destroyed modal instance"],"exampleFix":"// before\napp.openSearch({ initialQuery: q }); // throws if modal not ready\n// after\nif (!app.isSearchModalInitialized()) return;\napp.openSearch({ initialQuery: q });","handlingStrategy":"try-catch","validationCode":"if (!app || typeof app.isSearchModalInitialized !== 'function' || !app.isSearchModalInitialized()) {\n  return; // defer search until boot completes\n}","typeGuard":"function canOpenSearch(app) {\n  return app != null && app.state?.searchModal != null;\n}","tryCatchPattern":"try {\n  app.openSearch({ initialQuery: q });\n} catch (e) {\n  if (e.message === 'Search modal is not initialised') {\n    // queue the search until app:ready, or retry after init\n  } else throw e;\n}","preventionTips":["Gate hotkey/programmatic search behind app boot completion","Initialize the search modal before registering its shortcuts","Listen for an app:ready/init event before enabling search UI","Handle teardown: null out listeners that can fire after modal destruction"],"tags":["lifecycle","initialization","ui","race-condition"],"backgroundTag":"component-not-initialized","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}