{"record":{"id":"67d975b384c39e4e","repo":"Freika/dawarich","slug":"invalid-tile-coordinates","errorCode":null,"errorMessage":"Invalid tile coordinates","messagePattern":"Invalid tile coordinates","errorType":"http","errorClass":"Points::VectorTileQuery::InvalidTileCoordinatesError","httpStatus":400,"severity":"warning","filePath":"app/queries/points/vector_tile_query.rb","lineNumber":253,"sourceCode":"\n  def with_statement_timeout\n    conn = Point.connection\n    conn.transaction do\n      conn.exec_query(\"SET LOCAL statement_timeout = #{QUERY_TIMEOUT_MS}\", 'VectorTileQuery Timeout')\n      yield conn\n    end\n  end\n\n  def parse_integer(value)\n    return value if value.is_a?(Integer)\n\n    Integer(value, 10)\n  rescue ArgumentError, TypeError\n    raise InvalidTileCoordinatesError\n  end\n\n  def validate_tile_coordinates!\n    raise InvalidTileCoordinatesError if z.negative? || z > 22\n\n    max_index = (1 << z) - 1\n    raise InvalidTileCoordinatesError if x.negative? || y.negative?\n    raise InvalidTileCoordinatesError if x > max_index || y > max_index\n  end\nend\n","sourceCodeStart":235,"sourceCodeEnd":260,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/queries/points/vector_tile_query.rb#L235-L260","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the requested z value in the server log - anything above 22 or below 0 is rejected by design","Fix the style/source maxzoom to at most 22 so the client never requests z>22","Verify the tile URL template placeholder order matches what the tiles controller parses","Coerce z/x/y to integers client-side before building the URL"],"exampleFix":"// before\nsource: { tiles: ['/api/v1/tiles/points/{z}/{x}/{y}.pbf'], maxzoom: 24 }\n\n// after\nsource: { tiles: ['/api/v1/tiles/points/{z}/{x}/{y}.pbf'], maxzoom: 22 }","handlingStrategy":"validation","validationCode":"function isValidTileZ(z) {\n  const n = Number(z)\n  return Number.isInteger(n) && n >= 0 && n <= 22\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Cap the map source's maxzoom at 22 so clients never request z>22","Coerce z/x/y to integers before building tile URLs (fractional zooms fail parse_integer server-side)","Keep URL template placeholder order in sync with the tiles route"],"tags":["maps","vector-tiles","maplibre","validation","coordinates"],"backgroundTag":"invalid-tile-coordinates","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}