# How to Debug Issues in GeoLibre: A Step-by-Step Guide for the Open Source Geospatial Library

> Debug GeoLibre issues effectively with our step-by-step guide. Inspect Zustand store and MapLibre-GL instance to swiftly resolve state and rendering bugs in this open source geospatial library.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-16

---

**Use the browser console to inspect the Zustand store via `useAppStore.getState()` and the MapLibre-GL instance via `window.mapController.getMap()` to quickly isolate state versus rendering bugs in GeoLibre.**

GeoLibre is an open-source geospatial visualization library built as an **npm workspaces monorepo**. Effective debugging requires understanding two core pillars: the **Zustand state store** that manages all UI and project state, and the **MapController** that wraps MapLibre-GL for map rendering. This guide walks you through proven techniques to debug issues in GeoLibre based on its actual source code architecture.

## Understanding GeoLibre's Debug Architecture

Before diving into specific techniques, recognize that GeoLibre splits responsibilities cleanly between state management and map rendering. Most bugs fall into one of three categories:

- **Store-related**: UI state, layer visibility, project metadata, or undo/redo history
- **Map-related**: Basemap loading, layer syncing, story-map playback, or control visibility
- **Integration bugs**: State and map becoming out of sync

The following sections target each area with concrete commands and file references.

## Debugging the Zustand Store (`useAppStore`)

The central store lives in `@geolibre/core/src/store.ts` and holds all application state through the `useAppStore` hook. This is your first stop for most debugging scenarios.

### Inspect Store State in the Console

Dump the complete state to verify data integrity:

```javascript
// Browser console command
useAppStore.getState()

```

For filtered inspection, target specific slices:

```typescript
// Check only layers with visibility = false
const state = useAppStore.getState();
const hiddenLayers = state.layers.filter(l => !l.visible);
console.table(hiddenLayers.map(l => ({ id: l.id, name: l.name })));

```

### Verify Undo/Redo History

GeoLibre uses `zundo` for temporal state management. Check the history stack when bugs appear after multiple edits:

```javascript
const past = useAppStore.getState().temporal.getState().pastStates;
console.table(past.map(s => s.projectName));

```

The temporal wrapper is defined at lines 5-11 in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts), with `pruneHistoryBySize` available to prevent memory bloat.

### Test Actions Directly

Isolate whether a bug is store-related by calling actions manually:

```javascript
// Toggle basemap visibility
useAppStore.getState().setBasemapVisible(false);

// Update collaboration presence
useAppStore.getState().updateCollaborationPresence({ cursor: { x: 100, y: 200 }});

```

If the action works in console but fails in UI, the bug likely involves the component layer, not the store itself.

## Debugging the MapController

The `MapController` in `@geolibre/map/src/map-controller.ts` manages the MapLibre-GL instance and synchronizes it with store state. Access it globally via `window.mapController` in development builds.

### Access the MapLibre Instance

```javascript
const map = window.mapController?.getMap();
console.log(map.getStyle().layers.map(l => l.id));

```

This reveals all active layers, sources, and paint properties currently rendered.

### Force Layer Synchronization

When layers exist in the store but don't appear on the map, manually trigger a sync:

```javascript
window.mapController?.syncLayers(useAppStore.getState().layers);

```

The `syncLayers` method (lines 120-130 in [`map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/map-controller.ts)) handles the complex logic of diffing store layers against MapLibre layers.

### Debug Basemap Issues

Planetary and regional basemaps use sentinel URLs that `resolveMapStyle` expands. Test a planetary switch manually:

```javascript
import { useAppStore } from '@geolibre/core';

const moonSentinel = 'geolibre://basemap/planet/Moon';
useAppStore.getState().setBasemapStyleUrl(moonSentinel);
window.mapController?.setStyle(moonSentinel);

```

If this fails, check console warnings—unrecognized sentinels fall back to `DEFAULT_BASEMAP` with a warning (lines 81-89).

### Test Story-Map Playback

Verify story-map features independently of the UI:

```javascript
// Set layer opacity for a story chapter
window.mapController?.setStoryLayerOpacity('layer-id', 0.2);

// Apply camera from a story chapter
window.mapController?.applyStoryChapterCamera(chapterConfig);

```

These methods at lines 740-770 let you isolate whether story-map bugs are in the playback logic or the chapter data itself.

## Common Debugging Workflow

Follow this systematic approach to debug issues in GeoLibre efficiently:

1. **Start the dev server**

   ```bash
   npm run dev  # http://localhost:5173

   ```

2. **Reproduce and pause**

   Trigger the bug, then open DevTools without reloading.

3. **Inspect store state**

   Run `useAppStore.getState()` and verify the relevant data exists and is correctly structured.

4. **Check MapLibre state**

   Run `window.mapController?.getMap().getStyle()` and confirm layers/sources match the store.

5. **Trace the execution**

   Add `console.trace()` to store actions or use debugger breakpoints in:

   - [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) for state mutations
   - [`packages/map/src/map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts) for map operations

6. **Run targeted tests**

   ```bash
   # Frontend unit tests

   npm run test:frontend
   
   # Python processing tests

   npm run test:backend
   
   # Full UI regression

   npm run test:e2e
   ```

## Symptom-Based Debugging Reference

| Symptom | Likely Location | Key File/Function |
|---------|---------------|-------------------|
| Layer missing after grid resize | Store grid logic | [`store.ts`](https://github.com/opengeos/GeoLibre/blob/main/store.ts) → `setMapGrid` |
| Basemap won't switch to planet | Sentinel resolution | [`map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/map-controller.ts) → `applyPlanetaryBasemap` |
| Undo/redo consumes too much memory | History pruning | [`store.ts`](https://github.com/opengeos/GeoLibre/blob/main/store.ts) → `pruneHistoryBySize` |
| Story opacity not resetting | Style restoration | [`map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/map-controller.ts) → `restoreLayerStyles` |
| Plugin raster missing in export | Source detection | [`map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/map-controller.ts) → `getLayerRasterSource` |
| Control (compass, terrain) not appearing | DOM management | [`map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/map-controller.ts) → `setBuiltInControlVisible` |

## Advanced Techniques

### Override Network Requests

Capture failing Mapbox style fetches for debugging:

```javascript
const originalFetch = window.fetch;
window.fetch = async (input, init) => {
  if (String(input).includes('mapbox')) {
    console.log('Mapbox request:', input);
  }
  return originalFetch(input, init);
};

```

### Enable React DevTools Inspection

The `useAppStore` hook appears in React DevTools under **Hooks** → **useAppStore**, providing a visual tree of state that updates in real time as you interact with the application.

### Run Single Test with Coverage

For rapid iteration on specific logic:

```bash
node --import tsx --test tests/layer-control-order.test.ts

```

The coverage floor enforced by `test:frontend:coverage` ensures your debugging changes remain tested.

## Summary

- **Start with the store**: `useAppStore.getState()` reveals all UI and project state; actions can be called directly to isolate bugs
- **Verify MapLibre sync**: `window.mapController.getMap()` exposes the rendered map state; `syncLayers` forces reconciliation
- **Use sentinel debugging**: Planetary basemap failures log warnings and fall back to defaults—check `resolveMapStyle`
- **Leverage the test suite**: `npm run test:frontend`, `test:backend`, and `test:e2e` provide regression safety
- **Reference exact line numbers**: The source files [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) and [`packages/map/src/map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts) contain the definitive implementation

## Frequently Asked Questions

### How do I access the GeoLibre store outside of React components?

The `useAppStore` export from `@geolibre/core` exposes a `getState()` method callable from anywhere, including the browser console. Import the store in your component or access it globally in development to inspect or manipulate state directly without React re-renders.

### Why isn't my layer appearing on the map even though it's in the store?

The store and MapLibre can drift out of sync. Call `window.mapController?.syncLayers(useAppStore.getState().layers)` to force synchronization. If this fixes the issue, the bug is in the reactive subscription chain—likely in the component that should trigger sync on layer changes.

### How do I debug GeoLibre's undo/redo functionality?

Access the temporal state via `useAppStore.getState().temporal.getState()`. The `pastStates` and `futureStates` arrays contain full state snapshots. Use `console.table(pastStates.map(s => s.projectName))` to inspect history, and check `pruneHistoryBySize` in [`store.ts`](https://github.com/opengeos/GeoLibre/blob/main/store.ts) if memory usage grows unexpectedly.

### Where should I add logging when debugging GeoLibre basemap errors?

Add `console.log` or `console.warn` in [`packages/map/src/map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts) within `resolveMapStyle` (lines 63-92) and `applyPlanetaryBasemap`. Unknown sentinel URLs trigger warnings at lines 81-89, making this the ideal interception point for basemap debugging.