{"record":{"id":"f44fd23556ddbc7b","repo":"Freika/dawarich","slug":"trackslayer-animation-frame-error","errorCode":null,"errorMessage":"[TracksLayer] Animation frame error:","messagePattern":"\\[TracksLayer\\] Animation frame error:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/javascript/maps_maplibre/layers/tracks_layer.js","lineNumber":297,"sourceCode":"              ? Math.max(\n                  4,\n                  Math.min(30, Math.round(this.selectedTrackLength / 400)),\n                )\n              : 6\n\n          // Transparent base when segments visible so their colors show through\n          const baseColor = this.segmentsActive\n            ? \"rgba(255,255,255,0)\"\n            : undefined\n\n          this.map.setPaintProperty(\n            this.flowLayerId,\n            \"line-gradient\",\n            this._buildFlowGradient(phase, { baseColor, numDashes }),\n          )\n        }\n      } catch (e) {\n        console.warn(\"[TracksLayer] Animation frame error:\", e)\n      }\n\n      if (this.animationActive) {\n        this.animationFrame = requestAnimationFrame(animate)\n      }\n    }\n\n    this.animationFrame = requestAnimationFrame(animate)\n  }\n\n  /**\n   * Stop the flowing gradient animation\n   */\n  _stopFlowAnimation() {\n    this.animationActive = false\n    if (this.animationFrame) {\n      cancelAnimationFrame(this.animationFrame)\n      this.animationFrame = null","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/javascript/maps_maplibre/layers/tracks_layer.js#L279-L315","documentation":"TracksLayer animates a flowing line-gradient by calling map.setPaintProperty on every requestAnimationFrame while animationActive is true. If the flow layer disappears between frames — toggled off, style changed, map destroyed — setPaintProperty throws and each failing frame logs this warning while the loop keeps running (and keeps logging) until stopped.","triggerScenarios":"Toggling the tracks layer or segments mode off without stopping the flow animation; changing the base style which removes custom layers; Turbo navigation destroying the map mid-animation; a bad phase/numDashes value producing an invalid line-gradient expression that MapLibre rejects.","commonSituations":"Users switching layers while a track animation plays; style hot-swaps; long sessions where an animating track's layer is later removed; rapid toggling of the flowing-tracks setting.","solutions":["Stop the flow animation (cancel the rAF and clear animationActive) in the same code path that removes or hides the flow layer","Guard each frame with this.map.getLayer(this.flowLayerId) before setPaintProperty","In remove()/disconnect(), stop the animation and null this.animationFrame","Include a map-alive check (e.g. !this.map._removed) in the loop condition"],"exampleFix":"// before\nthis.map.setPaintProperty(this.flowLayerId, \"line-gradient\", gradient)\n// after\nif (this.animationActive && !this.map._removed && this.map.getLayer(this.flowLayerId)) {\n  this.map.setPaintProperty(this.flowLayerId, \"line-gradient\", gradient)\n} else {\n  this.stopFlowAnimation()\n  return\n}","handlingStrategy":"type-guard","validationCode":"const flowLayerExists =\n  this.animationActive &&\n  this.map &&\n  !this.map._removed &&\n  typeof this.map.getLayer === \"function\" &&\n  this.map.getLayer(this.flowLayerId) !== undefined\nif (!flowLayerExists) {\n  this.stopFlowAnimation()\n  return\n}","typeGuard":"/** @param {object} map @param {string} layerId @returns {boolean} */\nfunction hasLayer(map, layerId) {\n  return Boolean(\n    map &&\n    !map._removed &&\n    typeof map.getLayer === \"function\" &&\n    map.getLayer(layerId)\n  )\n}","tryCatchPattern":"try {\n  this.map.setPaintProperty(this.flowLayerId, \"line-gradient\", gradient)\n} catch (e) {\n  console.warn(\"[TracksLayer] Animation frame error:\", e)\n  this.stopFlowAnimation() // stop the loop instead of logging every frame\n}","preventionTips":["Pair every requestAnimationFrame loop with a cancellation path that runs on layer removal and map teardown","Check map.getLayer before any per-frame paint property call","Stop animations in the layer's remove()/disconnect lifecycle, not only on user toggle"],"tags":["maplibre","animation","requestanimationframe","tracks-layer","setpaintproperty"],"backgroundTag":"maplibre-setpaintproperty-error","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}