getProjection() in @deck.gl/mapbox can throw instead of returning undefined when called before the map's style is assigned

Open Beginner friendly
#10,549 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
react, typescript
Domain
frontend

Research direction

Start in deck-utils.ts and trace the getProjection callers mentioned in MapboxOverlay#_onAddInterleaved and getDefaultView. Reproduce the styleless-map timing case, then verify that projection lookup no longer propagates an early getProjection error and callers retain their not-ready fallback behavior.

Written by the indexing model from the issue text.

Description

Description

getProjection() in @deck.gl/mapbox's deck-utils.ts can throw when called
before the underlying map's style is assigned, instead of returning
undefined the way its callers (e.g. MapboxOverlay#_onAddInterleaved /
getDefaultView) expect for "not ready yet, default to mercator".

export function getProjection(map: Map): 'mercator' | 'globe' {
  const projection = map.getProjection?.();
  // ...

This guards against the method being absent (?.), but not against the
call itself throwing.

Repro
  • @deck.gl/mapbox@9.3.9, maplibre-gl@5.24, react-map-gl (maplibre entry
    point), React 19, MapboxOverlay in interleaved mode via useControl.
  • Under React 19 StrictMode, the dev-only double-invoke of mount effects
    adds, removes, and re-adds the same overlay instance within a single mount.
    On the third addControl call, map.getProjection() is invoked before the
    map's style has been attached.
  • maplibre-gl@5.24's own Map.prototype.getProjection() is
    return this.style.getProjection(); with no guard on this.style — so
    calling it before the style exists throws
    (Cannot read properties of undefined (reading 'getProjection')) rather
    than returning undefined.
  • Since @deck.gl/mapbox's getProjection() only guards the method being
    absent, the throw propagates out of _onAddInterleaved, surfacing as an
    uncaught error in the host application.

Reproduced live with Playwright (response-body interception instrumenting
getProjection): with StrictMode active, exactly 3 getProjection calls
fire on mount, and the 3rd hits a still-styleless map.

Related

PR #9794 fixed a similar timing/undefined issue in the later
_handleStyleChange callback path ("getDefaultView is called too early,
and getProjection returns undefined... the bug only happens with React,
in interleaved mode"), but the fix doesn't cover this earlier onAdd-time
call site, which can throw rather than return undefined.

Suggested fix

Wrap the map.getProjection?.() call in a try/catch inside getProjection(),
treating a throw the same as the already-handled undefined case:

export function getProjection(map: Map): 'mercator' | 'globe' {
  let projection;
  try {
    projection = map.getProjection?.();
  } catch {
    projection = undefined;
  }
  const type =
    // maplibre projection spec
    projection?.type ||
    // ...

We've worked around this locally with a pnpm patch applying exactly this
change to @deck.gl/mapbox@9.3.9 and would be happy to open a PR with it if
that's useful.

Dominant language
TypeScript
Stars
14.6k
Forks
2.3k
Avg merge
2d 9h
Merged PRs (30d)
42

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from visgl/deck.gl

All issues in visgl/deck.gl

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.