Freika/dawarich · warning

[TracksLayer] Animation frame error:

Error message

[TracksLayer] Animation frame error:

What it means

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.

Source

Thrown at app/javascript/maps_maplibre/layers/tracks_layer.js:297

              ? Math.max(
                  4,
                  Math.min(30, Math.round(this.selectedTrackLength / 400)),
                )
              : 6

          // Transparent base when segments visible so their colors show through
          const baseColor = this.segmentsActive
            ? "rgba(255,255,255,0)"
            : undefined

          this.map.setPaintProperty(
            this.flowLayerId,
            "line-gradient",
            this._buildFlowGradient(phase, { baseColor, numDashes }),
          )
        }
      } catch (e) {
        console.warn("[TracksLayer] Animation frame error:", e)
      }

      if (this.animationActive) {
        this.animationFrame = requestAnimationFrame(animate)
      }
    }

    this.animationFrame = requestAnimationFrame(animate)
  }

  /**
   * Stop the flowing gradient animation
   */
  _stopFlowAnimation() {
    this.animationActive = false
    if (this.animationFrame) {
      cancelAnimationFrame(this.animationFrame)
      this.animationFrame = null

View on GitHub (pinned to 97fad417c5)

Solutions

  1. Stop the flow animation (cancel the rAF and clear animationActive) in the same code path that removes or hides the flow layer
  2. Guard each frame with this.map.getLayer(this.flowLayerId) before setPaintProperty
  3. In remove()/disconnect(), stop the animation and null this.animationFrame
  4. Include a map-alive check (e.g. !this.map._removed) in the loop condition

Example fix

// before
this.map.setPaintProperty(this.flowLayerId, "line-gradient", gradient)
// after
if (this.animationActive && !this.map._removed && this.map.getLayer(this.flowLayerId)) {
  this.map.setPaintProperty(this.flowLayerId, "line-gradient", gradient)
} else {
  this.stopFlowAnimation()
  return
}
Defensive patterns

Strategy: type-guard

Validate before calling

const flowLayerExists =
  this.animationActive &&
  this.map &&
  !this.map._removed &&
  typeof this.map.getLayer === "function" &&
  this.map.getLayer(this.flowLayerId) !== undefined
if (!flowLayerExists) {
  this.stopFlowAnimation()
  return
}

Type guard

/** @param {object} map @param {string} layerId @returns {boolean} */
function hasLayer(map, layerId) {
  return Boolean(
    map &&
    !map._removed &&
    typeof map.getLayer === "function" &&
    map.getLayer(layerId)
  )
}

Try / catch

try {
  this.map.setPaintProperty(this.flowLayerId, "line-gradient", gradient)
} catch (e) {
  console.warn("[TracksLayer] Animation frame error:", e)
  this.stopFlowAnimation() // stop the loop instead of logging every frame
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Freika/dawarich@97fad417c5 (2026-08-21). Data as JSON: /api/errors/f44fd23556ddbc7b. Report an issue: GitHub.