{"record":{"id":"411abd091ae918af","repo":"Freika/dawarich","slug":"failed-to-add-photos-layer","errorCode":null,"errorMessage":"Failed to add photos layer:","messagePattern":"Failed to add photos layer:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/javascript/controllers/maps/maplibre/layer_manager.js","lineNumber":76,"sourceCode":"    // Layer order matters - layers added first render below layers added later\n    // Order: scratch (bottom) -> heatmap -> areas -> tracks -> routes (visual) -> visits -> places -> photos -> family -> points -> routes-hit (interaction) -> recent-point (top) -> fog (canvas overlay)\n    // Note: routes-hit is above points visually but points dragging takes precedence via event ordering\n\n    await this._addScratchLayer(pointsGeoJSON)\n    this._addHeatmapLayer(pointsGeoJSON)\n    this._addHexagonLayer()\n    this._addAreasLayer(areasGeoJSON)\n    this._addTracksLayer(tracksGeoJSON)\n    this._addRoutesLayer(routesGeoJSON)\n    this._addFlightsLayer(flightsGeoJSON)\n    this._addVisitsLayer(visitsGeoJSON)\n    this._addPlacesLayer(placesGeoJSON)\n\n    // Add photos layer with error handling (async, might fail loading images)\n    try {\n      await this._addPhotosLayer(photosGeoJSON)\n    } catch (error) {\n      console.warn(\"Failed to add photos layer:\", error)\n    }\n\n    this._addFamilyLayer()\n    this._addAnomaliesLayer()\n    this._addPointsMvtLayer()\n    this._addPointsLayer(pointsGeoJSON)\n    this._addRoutesHitLayer() // Add hit target layer after points, will be on top visually\n    this._addRecentPointLayer()\n    this._addReplayMarkerLayer()\n    this._addFogLayer(pointsGeoJSON)\n\n    performanceMonitor.measure(\"add-layers\")\n  }\n\n  /**\n   * Setup event handlers for layer interactions\n   * Only sets up handlers once to prevent duplicates\n   */","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/javascript/controllers/maps/maplibre/layer_manager.js#L58-L94","documentation":"LayerManager.addLayers() adds every layer synchronously except photos, which is awaited inside a try/catch because loading photo images is async and can fail. If _addPhotosLayer throws (broken image URLs, EXIF/decode failures, malformed GeoJSON), the warning is logged and the remaining layers (family, anomalies, points, routes hit, recent point, replay, fog) are still added — only photo markers go missing.","triggerScenarios":"photosGeoJSON containing features with broken or expired image URLs; a photo whose EXIF parsing or image decode fails inside the photos layer; malformed photos GeoJSON (missing geometry or properties); object storage/CDN unreachable so image prefetch rejects.","commonSituations":"Self-hosted Dawarich with S3/MinIO storage misconfigured (wrong or unsigned URLs); photos synced from Immich or GPX imports with stale URLs; reverse proxy blocking image content types; a single corrupt photo aborting the whole batch.","solutions":["Expand the logged error object — it distinguishes URL fetch failures from decode/EXIF failures","Open the photos API endpoint in a browser and confirm every URL in the GeoJSON resolves (watch for 403/404 from storage)","Fix or remove the single bad photo record, then reload — often one broken image aborts the entire batch","If images are external, ensure CORS or proxy headers allow the map origin"],"exampleFix":"// before\ntry {\n  await this._addPhotosLayer(photosGeoJSON)\n} catch (error) {\n  console.warn(\"Failed to add photos layer:\", error)\n}\n// after\ntry {\n  await this._addPhotosLayer(photosGeoJSON)\n} catch (error) {\n  console.warn(\"Failed to add photos layer:\", error)\n  // Degrade per-photo so one broken image cannot blank the whole layer\n  this._addPhotosLayer(photosGeoJSON, { skipFailingImages: true }).catch(() => {})\n}","handlingStrategy":"try-catch","validationCode":"const validPhotos = {\n  type: \"FeatureCollection\",\n  features: (photosGeoJSON?.features || []).filter(\n    (f) =>\n      f?.geometry &&\n      typeof f.properties?.image_url === \"string\" &&\n      f.properties.image_url.length > 0\n  ),\n}\nawait this._addPhotosLayer(validPhotos)","typeGuard":null,"tryCatchPattern":"try {\n  await this._addPhotosLayer(photosGeoJSON)\n} catch (error) {\n  console.warn(\"Failed to add photos layer:\", error)\n  // Photos are non-critical: continue adding the remaining layers\n}","preventionTips":["Validate photo feature URLs before they reach the layer","Isolate async image loading per photo so one failure cannot reject the whole layer add","Keep the photos layer behind the same feature flag as photo import so users without photos skip it entirely"],"tags":["maplibre","dawarich","photos-layer","image-loading","geojson"],"backgroundTag":"map-layer-add-failed","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}