Freika/dawarich · warning · Points::VectorTileQuery::InvalidTileCoordinatesError

Invalid tile coordinates

Error message

Invalid tile coordinates

What it means

Points::VectorTileQuery raises InvalidTileCoordinatesError (message 'Invalid tile coordinates') when the zoom level z is negative or greater than 22. Inputs pass through parse_integer first (non-integer strings like 'abc' or floats like '5.5' raise the same error from Integer(value, 10)), then validate_tile_coordinates! enforces z in [0, 22]. The tiles controller rescues this error (app/controllers/api/v1/tiles/points_controller.rb:53) and answers 400, so this is a client-malformed-URL rejection, not a server fault.

Source

Thrown at app/queries/points/vector_tile_query.rb:253

  def with_statement_timeout
    conn = Point.connection
    conn.transaction do
      conn.exec_query("SET LOCAL statement_timeout = #{QUERY_TIMEOUT_MS}", 'VectorTileQuery Timeout')
      yield conn
    end
  end

  def parse_integer(value)
    return value if value.is_a?(Integer)

    Integer(value, 10)
  rescue ArgumentError, TypeError
    raise InvalidTileCoordinatesError
  end

  def validate_tile_coordinates!
    raise InvalidTileCoordinatesError if z.negative? || z > 22

    max_index = (1 << z) - 1
    raise InvalidTileCoordinatesError if x.negative? || y.negative?
    raise InvalidTileCoordinatesError if x > max_index || y > max_index
  end
end

View on GitHub (pinned to 97fad417c5)

Solutions

  1. Check the requested z value in the server log - anything above 22 or below 0 is rejected by design
  2. Fix the style/source maxzoom to at most 22 so the client never requests z>22
  3. Verify the tile URL template placeholder order matches what the tiles controller parses
  4. Coerce z/x/y to integers client-side before building the URL

Example fix

// before
source: { tiles: ['/api/v1/tiles/points/{z}/{x}/{y}.pbf'], maxzoom: 24 }

// after
source: { tiles: ['/api/v1/tiles/points/{z}/{x}/{y}.pbf'], maxzoom: 22 }
Defensive patterns

Strategy: validation

Validate before calling

function isValidTileZ(z) {
  const n = Number(z)
  return Number.isInteger(n) && n >= 0 && n <= 22
}

Prevention

When it happens

Trigger: GET of a vector tile URL like /api/v1/tiles/points/23/1/1.pbf with z=23+ (MapLibre can request zooms beyond source maxzoom if the style's maxzoom is set high or a custom URL template mangles the order of z/x/y, e.g. swapping z and x so a huge x lands in the z slot), negative z from a computed Math.floor bug, or a fractional zoom string.

Common situations: Custom URL templates with placeholders in the wrong order ({z}/{x}/{y} vs the params the backend expects), styles declaring maxzoom above 22, camera animations that clamp tiles at fractional negative zooms, hand-testing tile endpoints with arbitrary numbers.

Related errors


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