Skip to content

fix(tile_layer): Treat the transparent placeholder as a failed load - #2268

Open
nebkat wants to merge 2 commits into
fleaflet:masterfrom
nebkat:fix/abandoned-tile-placeholder
Open

nebkat wants to merge 2 commits into
fleaflet:masterfrom
nebkat:fix/abandoned-tile-placeholder

Conversation

@nebkat

@nebkat nebkat commented Sep 17, 2026

Copy link
Copy Markdown

Builds on #2260 — the first commit here is that PR. This only makes sense with it: without that fix a load error still counts as covering, so on its own this would turn a blank tile into a hole.

A TileProvider that cannot throw returns TileProvider.transparentImage instead, usually when a request is cancelled as its tile leaves the viewport. That arrives as an ordinary successful 1x1 decode, so the tile is marked loaded while painting nothing, and counts as covering its area.

The cancelled tile itself is being disposed, so it is not the one that suffers. A tile created while that request is still in flight is: it resolves the same URL, attaches to the same pending image stream, and receives the placeholder. It is live, it claims to be loaded, and it paints nothing, so the layer drops the ancestor that was standing in for it.

Report it as a load error so that ancestor is kept, and evict it for the benefit of providers that return the placeholder without evicting it themselves. It reaches errorTileCallback but is not printed — abandoning a load is routine while panning.

nebkat and others added 2 commits September 17, 2026 23:23
… error image

readyToDisplay documents itself as true when "loading errored but an error image
is configured", but _display() set it on any error regardless. A tile that
failed and has nothing to paint therefore counted as covering its area, so
renderTiles discarded the coarser ancestor standing in for it and the area went
blank instead of staying upscaled. Services whose coverage depth varies by
location show this plainly: past the deepest level available at a given place
the tiles 404, and zooming in lost detail that was already on screen.

Set it from errorImage, as documented. Tiles that have an error image are
unaffected, and must stay ready — opacity is gated on this, so the error image
would otherwise never be painted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A TileProvider that cannot throw returns TileProvider.transparentImage instead,
usually when a request is cancelled as its tile leaves the viewport. That
arrives as an ordinary successful 1x1 decode, so the tile is marked loaded while
painting nothing, and counts as covering its area.

The cancelled tile itself is being disposed, so it is not the one that suffers.
A tile created while that request is still in flight is: it resolves the same
URL, attaches to the same pending image stream, and receives the placeholder. It
is live, it claims to be loaded, and it paints nothing, so the layer drops the
ancestor that was standing in for it.

Report it as a load error so that ancestor is kept, and evict it for the benefit
of providers that return the placeholder without evicting it themselves. It
reaches errorTileCallback but is not printed: abandoning a load is routine while
panning.

Depends on the readyToDisplay fix in the previous commit; without it a load
error still counts as covering and this changes a blank tile into a hole.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant