Skip to content

Conversation

@zstadler
Copy link
Contributor

@zstadler zstadler commented Jan 5, 2026

When processing regional extracts, incomplete OSM relations (missing ways or nodes) are expected and common. Previously, these exceptions were logged with full stack traces, cluttering logs and obscuring more critical issues.

This change:

  • Adds a helper method to detect incomplete relation exceptions
  • Logs incomplete relation exceptions as warnings without stack traces
  • Keeps full error logging with stack traces for unexpected errors

The fix handles exceptions related to:

  • Missing nodes ("Missing location for node")
  • Incomplete multipolygons (empty rings, not closed, missing ways)
  • GeometryException with stats indicating incomplete relations

Includes a test that verifies incomplete relations are logged as warnings without stack traces, and fails if the fix is reverted.

Fixes #1390

When processing regional extracts, incomplete OSM relations (missing
ways or nodes) are expected and common. Previously, these exceptions
were logged with full stack traces, cluttering logs and obscuring
more critical issues.

This change:
- Adds a helper method to detect incomplete relation exceptions
- Logs incomplete relation exceptions as warnings without stack traces
- Keeps full error logging with stack traces for unexpected errors

The fix handles exceptions related to:
- Missing nodes ("Missing location for node")
- Incomplete multipolygons (empty rings, not closed, missing ways)
- GeometryException with stats indicating incomplete relations

Includes a test that verifies incomplete relations are logged as
warnings without stack traces, and fails if the fix is reverted.

Fixes onthegomap#1390
@github-actions
Copy link

github-actions bot commented Jan 5, 2026

This Branch 235ee19 Base d327a49
0:01:08 DEB [archive] - Tile stats:
0:01:08 DEB [archive] - Biggest tiles (gzipped)
1. 14/4942/6092 (160k) https://onthegomap.github.io/planetiler-demo/#14.5/41.82864/-71.40015 (poi:88k)
2. 9/154/190 (148k) https://onthegomap.github.io/planetiler-demo/#9.5/41.77078/-71.36719 (landcover:85k)
3. 10/308/381 (137k) https://onthegomap.github.io/planetiler-demo/#10.5/41.63994/-71.54297 (landcover:72k)
4. 10/308/380 (137k) https://onthegomap.github.io/planetiler-demo/#10.5/41.90214/-71.54297 (landcover:66k)
5. 14/4941/6092 (117k) https://onthegomap.github.io/planetiler-demo/#14.5/41.82864/-71.42212 (poi:69k)
6. 14/4941/6093 (117k) https://onthegomap.github.io/planetiler-demo/#14.5/41.81227/-71.42212 (building:62k)
7. 14/4940/6092 (101k) https://onthegomap.github.io/planetiler-demo/#14.5/41.82864/-71.44409 (building:92k)
8. 14/4946/6112 (99k) https://onthegomap.github.io/planetiler-demo/#14.5/41.50035/-71.31226 (building:60k)
9. 11/616/762 (99k) https://onthegomap.github.io/planetiler-demo/#11.5/41.7057/-71.63086 (landcover:71k)
10. 11/616/764 (97k) https://onthegomap.github.io/planetiler-demo/#11.5/41.44269/-71.63086 (landcover:74k)
0:01:08 DEB [archive] - Max tile sizes
                      z0    z1    z2    z3    z4    z5    z6    z7    z8    z9   z10   z11   z12   z13   z14   all
           boundary  151   336   409   544   872   350   464   571   808  1.6k    2k  6.8k  6.2k  5.6k  4.4k  6.8k
              water 7.7k  3.7k  8.6k  5.5k  2.6k  5.1k   15k   18k   16k   26k   15k   13k   17k   15k   12k   26k
              place    0     0   487   487   487   707   796    1k  1.6k  3.1k    6k  3.6k  1.8k   845   978    6k
            landuse    0     0     0     0   549   695  1.6k  6.7k   17k   44k   58k   49k   38k   19k   12k   58k
     transportation    0     0     0     0   418   968  1.4k  4.5k  6.4k   21k   15k   17k   64k   43k   34k   64k
           waterway    0     0     0     0   112   119     0     0     0  3.3k  2.4k    2k  2.1k  4.9k  2.4k  4.9k
               park    0     0     0     0     0     0  1.1k    4k  9.7k   18k   13k  8.2k  3.7k  3.4k  4.4k   18k
transportation_name    0     0     0     0     0     0   287   364  1.1k  1.9k  5.6k  4.8k  3.9k  3.5k   18k   18k
          landcover    0     0     0     0     0     0     0  9.7k   29k   85k   72k   81k   53k   30k   25k   85k
      mountain_peak    0     0     0     0     0     0     0  1.1k  1.8k  3.4k  4.4k  2.8k  1.4k  1.4k   869  4.4k
         water_name    0     0     0     0     0     0     0     0     0   486   461   433   452  1.2k  1.5k  1.5k
    aerodrome_label    0     0     0     0     0     0     0     0     0     0   666   289   273   221   221   666
            aeroway    0     0     0     0     0     0     0     0     0     0  1.6k    2k    3k  3.3k  2.8k  3.3k
                poi    0     0     0     0     0     0     0     0     0     0     0     0   589   586   88k   88k
           building    0     0     0     0     0     0     0     0     0     0     0     0     0   59k   92k   92k
        housenumber    0     0     0     0     0     0     0     0     0     0     0     0     0     0   35k   35k
          full tile 7.9k    4k  9.5k  6.5k  3.8k  6.3k   21k   41k   84k  201k  183k  135k  114k  124k  252k  252k
            gzipped 6.2k  3.5k  7.1k  5.2k  3.2k    5k   14k   29k   60k  148k  137k   99k   84k   89k  160k  160k
0:01:08 DEB [archive] -    Max tile: 252k (gzipped: 160k)
0:01:08 DEB [archive] -    Avg tile: 5.5k (gzipped: 4.1k) using weighted average based on OSM traffic
0:01:08 DEB [archive] -     # tiles: 4,115,032
0:01:08 DEB [archive] -  # features: 5,712,771
0:01:08 INF [archive] - Finished in 20s cpu:1m14s avg:3.7
0:01:08 INF [archive] -   read    1x(3% 0.6s wait:18s done:1s)
0:01:08 INF [archive] -   encode  4x(58% 12s wait:2s)
0:01:08 INF [archive] -   write   1x(21% 4s wait:14s)
0:01:08 INF [archive] - Finished in 1m9s cpu:3m47s gc:1s avg:3.3
0:01:08 INF [archive] - FINISHED!
0:01:08 INF [archive] - 
0:01:08 INF [archive] - ----------------------------------------
0:01:08 INF [archive] - data errors:
0:01:08 INF [archive] - 	render_snap_fix_input	16,924
0:01:08 INF [archive] - 	osm_multipolygon_missing_way	397
0:01:08 INF [archive] - 	osm_boundary_missing_way	55
0:01:08 INF [archive] - 	merge_snap_fix_input	16
0:01:08 INF [archive] - 	feature_polygon_osm_invalid_multipolygon_empty_after_fix	8
0:01:08 INF [archive] - 	feature_centroid_if_convex_osm_invalid_multipolygon_empty_after_fix	2
0:01:08 INF [archive] - 	render_snap_fix_input2	1
0:01:08 INF [archive] - 	omt_park_area_osm_invalid_multipolygon_empty_after_fix	1
0:01:08 INF [archive] - 	omt_fix_water_before_ne_intersect	1
0:01:08 INF [archive] - 	feature_point_on_surface_osm_invalid_multipolygon_empty_after_fix	1
0:01:08 INF [archive] - ----------------------------------------
0:01:08 INF [archive] - 	overall          1m9s cpu:3m47s gc:1s avg:3.3
0:01:08 INF [archive] - 	lake_centerlines 3s cpu:6s avg:2.3
0:01:08 INF [archive] - 	  read     1x(18% 0.5s done:2s)
0:01:08 INF [archive] - 	  process  4x(0% 0s done:2s)
0:01:08 INF [archive] - 	  write    1x(0% 0s done:2s)
0:01:08 INF [archive] - 	water_polygons   15s cpu:41s avg:2.7
0:01:08 INF [archive] - 	  read     1x(42% 6s done:7s)
0:01:08 INF [archive] - 	  process  4x(26% 4s wait:4s done:6s)
0:01:08 INF [archive] - 	  write    1x(3% 0.5s wait:9s done:5s)
0:01:08 INF [archive] - 	natural_earth    6s cpu:13s avg:2
0:01:08 INF [archive] - 	  read     1x(96% 6s)
0:01:08 INF [archive] - 	  process  4x(13% 0.8s wait:6s)
0:01:08 INF [archive] - 	  write    1x(0% 0s wait:6s)
0:01:08 INF [archive] - 	osm_pass1        2s cpu:7s avg:3.4
0:01:08 INF [archive] - 	  read     1x(2% 0s wait:2s)
0:01:08 INF [archive] - 	  parse    4x(35% 0.7s)
0:01:08 INF [archive] - 	  process  1x(70% 1s)
0:01:08 INF [archive] - 	osm_pass2        20s cpu:1m20s avg:3.9
0:01:08 INF [archive] - 	  read     1x(0% 0s wait:13s done:7s)
0:01:08 INF [archive] - 	  process  4x(76% 15s)
0:01:08 INF [archive] - 	  write    1x(2% 0.5s wait:20s)
0:01:08 INF [archive] - 	ne_lakes         0s cpu:0s avg:0
0:01:08 INF [archive] - 	boundaries       0s cpu:0s avg:2.2
0:01:08 INF [archive] - 	agg_stop         0s cpu:0s avg:0
0:01:08 INF [archive] - 	sort             1s cpu:3s avg:2.5
0:01:08 INF [archive] - 	  worker  1x(51% 0.7s)
0:01:08 INF [archive] - 	archive          20s cpu:1m14s avg:3.7
0:01:08 INF [archive] - 	  read    1x(3% 0.6s wait:18s done:1s)
0:01:08 INF [archive] - 	  encode  4x(58% 12s wait:2s)
0:01:08 INF [archive] - 	  write   1x(21% 4s wait:14s)
0:01:08 INF [archive] - ----------------------------------------
0:01:08 INF [archive] - 	archive	108MB
0:01:08 INF [archive] - 	features	293MB
-rw-r--r-- 1 runner runner 89M Jan  5 15:51 run.jar
0:01:08 DEB [archive] - Tile stats:
0:01:08 DEB [archive] - Biggest tiles (gzipped)
1. 14/4942/6092 (160k) https://onthegomap.github.io/planetiler-demo/#14.5/41.82864/-71.40015 (poi:88k)
2. 9/154/190 (148k) https://onthegomap.github.io/planetiler-demo/#9.5/41.77078/-71.36719 (landcover:85k)
3. 10/308/381 (137k) https://onthegomap.github.io/planetiler-demo/#10.5/41.63994/-71.54297 (landcover:72k)
4. 10/308/380 (137k) https://onthegomap.github.io/planetiler-demo/#10.5/41.90214/-71.54297 (landcover:66k)
5. 14/4941/6092 (117k) https://onthegomap.github.io/planetiler-demo/#14.5/41.82864/-71.42212 (poi:69k)
6. 14/4941/6093 (117k) https://onthegomap.github.io/planetiler-demo/#14.5/41.81227/-71.42212 (building:62k)
7. 14/4940/6092 (101k) https://onthegomap.github.io/planetiler-demo/#14.5/41.82864/-71.44409 (building:92k)
8. 14/4946/6112 (99k) https://onthegomap.github.io/planetiler-demo/#14.5/41.50035/-71.31226 (building:60k)
9. 11/616/762 (99k) https://onthegomap.github.io/planetiler-demo/#11.5/41.7057/-71.63086 (landcover:71k)
10. 11/616/764 (97k) https://onthegomap.github.io/planetiler-demo/#11.5/41.44269/-71.63086 (landcover:74k)
0:01:08 DEB [archive] - Max tile sizes
                      z0    z1    z2    z3    z4    z5    z6    z7    z8    z9   z10   z11   z12   z13   z14   all
           boundary  151   336   409   544   872   350   464   571   808  1.6k    2k  6.8k  6.2k  5.6k  4.4k  6.8k
              water 7.7k  3.7k  8.6k  5.5k  2.6k  5.1k   15k   18k   16k   26k   15k   13k   17k   15k   12k   26k
              place    0     0   487   487   487   707   796    1k  1.6k  3.1k    6k  3.6k  1.8k   845   978    6k
            landuse    0     0     0     0   549   695  1.6k  6.7k   17k   44k   58k   49k   38k   19k   12k   58k
     transportation    0     0     0     0   418   968  1.4k  4.5k  6.4k   21k   15k   17k   64k   43k   34k   64k
           waterway    0     0     0     0   112   119     0     0     0  3.3k  2.4k    2k  2.1k  4.9k  2.4k  4.9k
               park    0     0     0     0     0     0  1.1k    4k  9.7k   18k   13k  8.2k  3.7k  3.4k  4.4k   18k
transportation_name    0     0     0     0     0     0   287   364  1.1k  1.9k  5.6k  4.8k  3.9k  3.5k   18k   18k
          landcover    0     0     0     0     0     0     0  9.7k   29k   85k   72k   81k   53k   30k   25k   85k
      mountain_peak    0     0     0     0     0     0     0  1.1k  1.8k  3.4k  4.4k  2.8k  1.4k  1.4k   869  4.4k
         water_name    0     0     0     0     0     0     0     0     0   486   461   433   452  1.2k  1.5k  1.5k
    aerodrome_label    0     0     0     0     0     0     0     0     0     0   666   289   273   221   221   666
            aeroway    0     0     0     0     0     0     0     0     0     0  1.6k    2k    3k  3.3k  2.8k  3.3k
                poi    0     0     0     0     0     0     0     0     0     0     0     0   589   586   88k   88k
           building    0     0     0     0     0     0     0     0     0     0     0     0     0   59k   92k   92k
        housenumber    0     0     0     0     0     0     0     0     0     0     0     0     0     0   35k   35k
          full tile 7.9k    4k  9.5k  6.5k  3.8k  6.3k   21k   41k   84k  201k  183k  135k  114k  124k  252k  252k
            gzipped 6.2k  3.5k  7.1k  5.2k  3.2k    5k   14k   29k   60k  148k  137k   99k   84k   89k  160k  160k
0:01:08 DEB [archive] -    Max tile: 252k (gzipped: 160k)
0:01:08 DEB [archive] -    Avg tile: 5.5k (gzipped: 4.1k) using weighted average based on OSM traffic
0:01:08 DEB [archive] -     # tiles: 4,115,032
0:01:08 DEB [archive] -  # features: 5,712,771
0:01:08 INF [archive] - Finished in 20s cpu:1m14s avg:3.7
0:01:08 INF [archive] -   read    1x(3% 0.6s wait:18s done:1s)
0:01:08 INF [archive] -   encode  4x(57% 11s wait:2s)
0:01:08 INF [archive] -   write   1x(21% 4s wait:14s)
0:01:08 INF [archive] - Finished in 1m8s cpu:3m48s gc:1s avg:3.4
0:01:08 INF [archive] - FINISHED!
0:01:08 INF [archive] - 
0:01:08 INF [archive] - ----------------------------------------
0:01:08 INF [archive] - data errors:
0:01:08 INF [archive] - 	render_snap_fix_input	16,924
0:01:08 INF [archive] - 	osm_multipolygon_missing_way	397
0:01:08 INF [archive] - 	osm_boundary_missing_way	55
0:01:08 INF [archive] - 	merge_snap_fix_input	16
0:01:08 INF [archive] - 	feature_polygon_osm_invalid_multipolygon_empty_after_fix	8
0:01:08 INF [archive] - 	feature_centroid_if_convex_osm_invalid_multipolygon_empty_after_fix	2
0:01:08 INF [archive] - 	render_snap_fix_input2	1
0:01:08 INF [archive] - 	omt_park_area_osm_invalid_multipolygon_empty_after_fix	1
0:01:08 INF [archive] - 	omt_fix_water_before_ne_intersect	1
0:01:08 INF [archive] - 	feature_point_on_surface_osm_invalid_multipolygon_empty_after_fix	1
0:01:08 INF [archive] - ----------------------------------------
0:01:08 INF [archive] - 	overall          1m8s cpu:3m48s gc:1s avg:3.4
0:01:08 INF [archive] - 	lake_centerlines 2s cpu:5s avg:2.5
0:01:08 INF [archive] - 	  read     1x(22% 0.5s done:2s)
0:01:08 INF [archive] - 	  process  4x(0% 0s done:2s)
0:01:08 INF [archive] - 	  write    1x(0% 0s done:1s)
0:01:08 INF [archive] - 	water_polygons   15s cpu:43s avg:2.8
0:01:08 INF [archive] - 	  read     1x(42% 6s done:7s)
0:01:08 INF [archive] - 	  process  4x(27% 4s wait:4s done:5s)
0:01:08 INF [archive] - 	  write    1x(3% 0.5s wait:10s done:5s)
0:01:08 INF [archive] - 	natural_earth    6s cpu:13s avg:2.1
0:01:08 INF [archive] - 	  read     1x(95% 6s)
0:01:08 INF [archive] - 	  process  4x(13% 0.8s wait:6s)
0:01:08 INF [archive] - 	  write    1x(0% 0s wait:6s)
0:01:08 INF [archive] - 	osm_pass1        2s cpu:7s avg:3.4
0:01:08 INF [archive] - 	  read     1x(2% 0s wait:2s)
0:01:08 INF [archive] - 	  parse    4x(33% 0.7s)
0:01:08 INF [archive] - 	  process  1x(72% 2s)
0:01:08 INF [archive] - 	osm_pass2        20s cpu:1m21s avg:4
0:01:08 INF [archive] - 	  read     1x(0% 0s wait:13s done:7s)
0:01:08 INF [archive] - 	  process  4x(77% 16s)
0:01:08 INF [archive] - 	  write    1x(2% 0.4s wait:20s)
0:01:08 INF [archive] - 	ne_lakes         0s cpu:0s avg:0
0:01:08 INF [archive] - 	boundaries       0s cpu:0s avg:1.4
0:01:08 INF [archive] - 	agg_stop         0s cpu:0s avg:0
0:01:08 INF [archive] - 	sort             1s cpu:3s avg:2.5
0:01:08 INF [archive] - 	  worker  1x(55% 0.7s)
0:01:08 INF [archive] - 	archive          20s cpu:1m14s avg:3.7
0:01:08 INF [archive] - 	  read    1x(3% 0.6s wait:18s done:1s)
0:01:08 INF [archive] - 	  encode  4x(57% 11s wait:2s)
0:01:08 INF [archive] - 	  write   1x(21% 4s wait:14s)
0:01:08 INF [archive] - ----------------------------------------
0:01:08 INF [archive] - 	archive	108MB
0:01:08 INF [archive] - 	features	293MB
-rw-r--r-- 1 runner runner 109M Jan  5 15:52 run.jar

Full logs: https://github.com/onthegomap/planetiler/actions/runs/20720775627

- Use pattern matching for instanceof (java:S6201)
- Use try-with-resources for TestAppender (java:S2093)
- Add comprehensive tests for isIncompleteRelationException method:
  - Test all message patterns (missing node, multipolygon errors, etc.)
  - Test GeometryException stat matching
  - Test exception cause chain recursion
  - Test edge cases (null, empty message)
- Add test for non-incomplete exceptions logged as ERROR

This should improve code coverage above the 50% threshold required by SonarCloud.
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 5, 2026

Comment on lines +324 to +328
if (isIncompleteRelationException(e)) {
LOGGER.warn("Incomplete OSM relation {}: {}", relation.id(), e.getMessage());
} else {
LOGGER.error("Error preprocessing OSM relation " + relation.id(), e);
}
Copy link
Contributor

@msbarry msbarry Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this! I already have some similar handling along these lines where these "verbose" exceptions are instances of "GeometryException.Verbose", and GeometryException has a built in geometryException.log("Error processing relation") or geometryException.log("Error processing relation", stats, "osm_relation_error") if we want to include these in the summary of geometry errors that gets printed at the end.

It seems like we might be able to replace this whole PR with just changing these 2 "catch (Exception e)" blocks to something like:

} catch (GeometryException e) {
  e.log("Error processing relation " + relation.id());
} catch (Exception e) {
  LOGGER.error("Error preprocessing OSM relation {}", relation.id(), e);
}

Copy link
Contributor Author

@zstadler zstadler Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frankly, I don't have a strong opinion about the appearance in the log, other than avoiding a scary ERROR and a useless stack trace.

Please feel free to change the PR code in any way you like.

Copy link
Contributor

@msbarry msbarry Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually could you share one or a few full stack traces that you're seeing? I tried adding that line but found that GeometryException is actually caught before that point and logged using that method that suppresses verbose logs already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a stack trace in #1390 (comment)

Copy link
Contributor

@msbarry msbarry Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I see, these geometry exceptions are normally caught and logged quietly (or not logged at all) but your profile asks for the area of a feature, and Unit.java calls applyAndWrapException which propagates any geometry exception that would have been suppressed very loudly.

What do you think the fallback should be when area/length fail to compute? Should it return 0? null? or omit the entire feature

Or more broadly, if any of the properties in a yaml file fail with an exception do you think we should just omit that property? or the entire feature?

Copy link
Contributor Author

@zstadler zstadler Jan 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are actually two questions here:

  1. Should an incomplete polygon be processed at all? For example, should its attributes be computed?
    IMO, if a potential feature does not have a valid geometry it should be dropped as soon as can be.

  2. How to handle (other) exceptions while computing attribute values?
    IMO, it would be best if there is a clear error message with the OSM feature id and type, the layer.feature.name and the attribute name.
    That should allow users to fix their definition/code.
    Silent drop is generally not a good option. On the other hand, keeping the current stack trace is not very helpful for the user, and an unnecessary support burden for you.

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.

[FEATURE] Unnecessary stack trace for incomplete OSM relations

2 participants