You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A tile whose glyph-range request fails is never rendered — silently — and the range is never retried
Platform: iOS (observed on 6.26.0 and 6.27.0, device + simulator); code path is core, so all platforms should be affected.
Summary
If the fetch for a glyph range fails (404, offline, misconfigured glyph URL), every vector tile whose labels reference that range permanently fails to render. MapLibre paints the overzoomed ancestor tile in its place, with no error surfaced beyond a single onGlyphsError log line. The failed range is also never re-requested for the lifetime of the process, so recovery is impossible even if the resource becomes available.
Real-world impact
We self-host glyphs with a partial range set (a common setup — openmaptiles/fonts subsets, offline bundles). Our style uses {name:latin}, and one bundled face was missing range 7680-7935 (Latin Extended Additional). Result: in San José, CA, every z14 tile containing a Vietnamese-named POI (e.g. "Phở 24") rendered as a blurry overzoomed z13 parent, while neighboring tiles with ASCII-only labels rendered normally. The geographic patchiness made this extremely hard to attribute — it presents as a data or cache problem, not a font problem. It took us two days and a per-request instrumentation harness to trace it to the one warning line.
voidGlyphManager::processResponse(const Response& res, const FontStack& fontStack, const GlyphRange& range) {
if (res.error) {
observer->onGlyphsError(fontStack, range, std::make_exception_ptr(std::runtime_error(res.error->message)));
return; // <-- request.req not cleared, requestors never notified
}
...
Two consequences:
The GlyphRequestors (the GeometryTileWorkers waiting on this range) are never notified, so the tile's symbol-layout dependency never resolves and the tile never becomes renderable. Fill/line layers that parsed fine are discarded along with the labels.
request.req remains non-null, so requestRange's if (request.req) return; guard suppresses any future attempt to fetch the range — including after connectivity returns. (This is also the underlying mechanism of Node.js render hangs when repeating request after glyph load failure #3169, where a second render request hangs after a glyph failure.)
Expected behavior
A missing glyph range should degrade gracefully, consistent with how missing glyphs within a successfully-loaded range behave: affected labels are skipped, everything else in the tile renders. Concretely:
On error, notify requestors so dependent tiles complete layout without the unavailable glyphs.
Clear/mark the failed request so it can be retried (immediately on the next tile that needs it, or with backoff) rather than being pinned forever.
Reproduction
Style with "glyphs" pointing at a server that 404s range 7680-7935 for the style's fontstack (serve 0-255 normally).
A vector tile containing a symbol-layer label with any character in U+1E00–U+1EFF (e.g. Phở).
Load the map over that tile at the tile's native zoom.
Observed: the tile never renders (ancestor shown); log shows one Failed to load glyph range 7680-7935 for font stack … line; the range is requested exactly once per process.
We're happy to submit a PR along the lines above (notify-requestors-on-error + retriable request state) if the approach sounds right to maintainers.
A tile whose glyph-range request fails is never rendered — silently — and the range is never retried
Platform: iOS (observed on 6.26.0 and 6.27.0, device + simulator); code path is core, so all platforms should be affected.
Summary
If the fetch for a glyph range fails (404, offline, misconfigured glyph URL), every vector tile whose labels reference that range permanently fails to render. MapLibre paints the overzoomed ancestor tile in its place, with no error surfaced beyond a single
onGlyphsErrorlog line. The failed range is also never re-requested for the lifetime of the process, so recovery is impossible even if the resource becomes available.Real-world impact
We self-host glyphs with a partial range set (a common setup — openmaptiles/fonts subsets, offline bundles). Our style uses
{name:latin}, and one bundled face was missing range7680-7935(Latin Extended Additional). Result: in San José, CA, every z14 tile containing a Vietnamese-named POI (e.g. "Phở 24") rendered as a blurry overzoomed z13 parent, while neighboring tiles with ASCII-only labels rendered normally. The geographic patchiness made this extremely hard to attribute — it presents as a data or cache problem, not a font problem. It took us two days and a per-request instrumentation harness to trace it to the one warning line.Mechanism (v6.26.0 / current main)
GlyphManager::processResponse(src/mbgl/text/glyph_manager.cpp):Two consequences:
GlyphRequestors (theGeometryTileWorkers waiting on this range) are never notified, so the tile's symbol-layout dependency never resolves and the tile never becomes renderable. Fill/line layers that parsed fine are discarded along with the labels.request.reqremains non-null, sorequestRange'sif (request.req) return;guard suppresses any future attempt to fetch the range — including after connectivity returns. (This is also the underlying mechanism of Node.js render hangs when repeating request after glyph load failure #3169, where a second render request hangs after a glyph failure.)Expected behavior
A missing glyph range should degrade gracefully, consistent with how missing glyphs within a successfully-loaded range behave: affected labels are skipped, everything else in the tile renders. Concretely:
Reproduction
"glyphs"pointing at a server that 404s range7680-7935for the style's fontstack (serve0-255normally).Phở).Observed: the tile never renders (ancestor shown); log shows one
Failed to load glyph range 7680-7935 for font stack …line; the range is requested exactly once per process.We're happy to submit a PR along the lines above (notify-requestors-on-error + retriable request state) if the approach sounds right to maintainers.