{"record":{"id":"629c450b8891c4d9","repo":"Freika/dawarich","slug":"eventhandlers-failed-to-highlight-track","errorCode":null,"errorMessage":"[EventHandlers] Failed to highlight track:","messagePattern":"\\[EventHandlers\\] Failed to highlight track:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/javascript/controllers/maps/maplibre/event_handlers.js","lineNumber":678,"sourceCode":"      if (trackPointFeatures.length > 0) return\n    }\n\n    const clickedFeature = e.features[0]\n    if (!clickedFeature) return\n\n    const properties = clickedFeature.properties\n    const fullFeature = this._getFullTrackFeature(properties) || clickedFeature\n    this.selectedTrackFeature = fullFeature\n\n    // Keep the on-map highlight + segment visualization — those are visual\n    // feedback for the click itself, independent of the info surface.\n    try {\n      const tracksLayer = this.controller.layerManager.getLayer(\"tracks\")\n      if (tracksLayer?.setSelectedTrack) {\n        tracksLayer.setSelectedTrack(fullFeature)\n      }\n    } catch (err) {\n      console.warn(\"[EventHandlers] Failed to highlight track:\", err)\n    }\n    this._loadTrackSegments(properties.id, fullFeature)\n\n    // Derive the day from the track's start. `start_at` comes from our own\n    // serializer as an ISO8601 string — safe to slice the date portion.\n    const startAt =\n      typeof properties.start_at === \"string\" ? properties.start_at : null\n    const date = startAt ? startAt.slice(0, 10) : null\n    const trackId = Number(properties.id)\n\n    document.dispatchEvent(\n      new CustomEvent(\"timeline:open-track\", {\n        detail: { trackId, date, startAt },\n      }),\n    )\n  }\n\n  /**","sourceCodeStart":660,"sourceCodeEnd":696,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/javascript/controllers/maps/maplibre/event_handlers.js#L660-L696","documentation":"Dawarich's MapLibre maps v2 event handler wraps the visual highlight of a clicked track in a defensive try/catch. After a track click it resolves the full feature, stores it as the selected track, and asks LayerManager for the 'tracks' layer to call setSelectedTrack(). Any exception inside that highlight step (layer not registered, map style not ready, layer removed mid-click) is caught and logged with this message; the click flow still continues to load segments and open the timeline.","triggerScenarios":"Clicking a track before LayerManager.addLayers() has registered the 'tracks' layer (style/tiles still loading); clicking while a style swap or layer toggle removes the layer underneath setSelectedTrack(); a refactor renaming the layer id so getLayer('tracks') returns an unexpected object; map torn down by Turbo navigation while the click handler chain is still running.","commonSituations":"Users clicking tracks immediately after page load on slow connections; Turbo cache restoring a stale page whose listeners outlive the map; layer ids changed during Maps v2 refactoring; highlight invoked after removeLayer during filter changes.","solutions":["Inspect the error object logged after the message — it names the exact layer or paint property that failed","Gate the highlight on readiness: check layerManager.getLayer('tracks') exists and map.isStyleLoaded() before calling setSelectedTrack","Make TracksLayer.setSelectedTrack defensive: early-return unless this.map.getLayer(this.layerId) exists","If it fires after Turbo navigation, clear handlers in the Stimulus controller's disconnect() so post-teardown clicks cannot reach a dead map"],"exampleFix":"// before\nconst tracksLayer = this.controller.layerManager.getLayer(\"tracks\")\nif (tracksLayer?.setSelectedTrack) {\n  tracksLayer.setSelectedTrack(fullFeature)\n}\n// after\nconst tracksLayer = this.controller.layerManager.getLayer(\"tracks\")\nconst mapReady = this.controller.map?.isStyleLoaded?.() === true\nif (tracksLayer?.setSelectedTrack && mapReady) {\n  tracksLayer.setSelectedTrack(fullFeature)\n} else {\n  console.debug(\"[EventHandlers] Tracks layer not ready, skipping highlight\")\n}","handlingStrategy":"try-catch","validationCode":"const layer = this.controller.layerManager.getLayer(\"tracks\")\nconst ready =\n  Boolean(layer && typeof layer.setSelectedTrack === \"function\") &&\n  this.controller.map?.isStyleLoaded?.() === true\nif (!ready) return // skip the highlight instead of risking a throw","typeGuard":"/** @param {object} controller @returns {boolean} */\nfunction canHighlightTrack(controller) {\n  const layer = controller.layerManager?.getLayer(\"tracks\")\n  return Boolean(\n    layer &&\n    typeof layer.setSelectedTrack === \"function\" &&\n    controller.map &&\n    typeof controller.map.getLayer === \"function\" &&\n    controller.map.styleLoaded()\n  )\n}","tryCatchPattern":"try {\n  tracksLayer.setSelectedTrack(fullFeature)\n} catch (err) {\n  // Visual-only feedback: log and keep the click flow going (segments + timeline)\n  console.warn(\"[EventHandlers] Failed to highlight track:\", err)\n}","preventionTips":["Register the tracks layer before binding click handlers that depend on it","Clear selected-track state and unbind click handlers in the controller's disconnect() lifecycle method","Treat highlight failures as non-fatal: never let them abort segment loading or the timeline dispatch"],"tags":["maplibre","dawarich","tracks-layer","ui-highlight","event-handlers"],"backgroundTag":"map-layer-not-found","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}