diff --git a/libs/kimath/include/geometry/shape_utils.h b/libs/kimath/include/geometry/shape_utils.h
index a18caffb2c..55fccce5bd 100644
--- a/libs/kimath/include/geometry/shape_utils.h
+++ b/libs/kimath/include/geometry/shape_utils.h
@@ -46,6 +46,7 @@ class HALF_LINE;
 class LINE;
 class SEG;
 class SHAPE_RECT;
+class SHAPE_POLY_SET;
 struct TYPED_POINT2I;
 
 namespace KIGEOM
@@ -162,4 +163,19 @@ std::vector<TYPED_POINT2I> GetCircleKeyPoints( const CIRCLE& aCircle, bool aIncl
  */
 SHAPE_LINE_CHAIN RectifyPolygon( const SHAPE_LINE_CHAIN& aPoly );
 
+
+/**
+ * Adds a hole to a polygon if it is valid (i.e. it has 3 or more points
+ * and a non-zero area.)
+ *
+ * This is important if you are reading in a polygon from a file and
+ * aren't sure if it will end up being valid. Leaving invalid holes in
+ * a SHAP_POLY_SET will violate the invariant that holes are non-null.
+ *
+ * @param aOutline is the outline to add the hole to.
+ * @param aHole is the hole to add.
+ * @return true if the hole was added, false if it was not.
+ */
+bool AddHoleIfValid( SHAPE_POLY_SET& aOutline, SHAPE_LINE_CHAIN&& aHole );
+
 } // namespace KIGEOM
\ No newline at end of file
diff --git a/libs/kimath/src/geometry/shape_utils.cpp b/libs/kimath/src/geometry/shape_utils.cpp
index dcb6854877..07f46b2e44 100644
--- a/libs/kimath/src/geometry/shape_utils.cpp
+++ b/libs/kimath/src/geometry/shape_utils.cpp
@@ -28,6 +28,7 @@
 #include <geometry/half_line.h>
 #include <geometry/line.h>
 #include <geometry/point_types.h>
+#include <geometry/shape_poly_set.h>
 #include <geometry/shape_rect.h>
 
 
@@ -375,4 +376,16 @@ SHAPE_LINE_CHAIN KIGEOM::RectifyPolygon( const SHAPE_LINE_CHAIN& aPoly )
     raOutline.Simplify();
 
     return raOutline;
-}
\ No newline at end of file
+}
+
+
+bool KIGEOM::AddHoleIfValid( SHAPE_POLY_SET& aOutline, SHAPE_LINE_CHAIN&& aHole )
+{
+    if( aHole.PointCount() < 3 || aHole.Area() == 0 )
+    {
+        return false;
+    }
+
+    aOutline.AddHole( std::move( aHole ) );
+    return true;
+}
diff --git a/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp b/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
index 8bdc2b1e21..1df674f931 100644
--- a/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
+++ b/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
@@ -50,9 +50,11 @@
 #include <zone.h>
 #include <common.h>
 #include <geometry/shape_arc.h>
+#include <geometry/shape_utils.h>
 #include <string_utils.h>
 #include <progress_reporter.h>
 #include <math/util.h>
+
 #include <wx/filename.h>
 
 
@@ -2790,8 +2792,6 @@ bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>
     if( aLine->segment.size() < 3 )
         return false;
 
-    int last_subseq = 0;
-    int hole_idx = -1;
     SHAPE_POLY_SET* zone_outline = nullptr;
     ZONE* zone = nullptr;
 
@@ -2841,7 +2841,29 @@ bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>
 
     zone_outline->NewOutline();
 
+    std::unique_ptr<SHAPE_LINE_CHAIN> pending_hole = nullptr;
+    SHAPE_LINE_CHAIN*                 active_chain = &zone_outline->Outline( 0 );
 
+    const auto add_hole_if_valid = [&]()
+    {
+        if( pending_hole )
+        {
+            pending_hole->SetClosed( true );
+
+            // If we get junk holes, assert, but don't add them to the zone, as that
+            // will cause crashes later.
+            if( !KIGEOM::AddHoleIfValid( *zone_outline, std::move( *pending_hole ) ) )
+            {
+                wxLogMessage( _( "Invalid hole with %d points in zone on layer %s with net %s" ),
+                              pending_hole->PointCount(), zone->GetLayerName(),
+                              zone->GetNetname() );
+            }
+
+            pending_hole.reset();
+        }
+    };
+
+    int last_subseq = 0;
     for( const auto& seg : aLine->segment )
     {
         if( seg->subseq > 0 && seg->subseq != last_subseq )
@@ -2851,33 +2873,52 @@ bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>
             if( aLine->lclass == "BOUNDARY" )
                 break;
 
-            hole_idx = zone_outline->AddHole( SHAPE_LINE_CHAIN{} );
-            last_subseq = seg->subseq;
+            add_hole_if_valid();
+            pending_hole = std::make_unique<SHAPE_LINE_CHAIN>();
+            active_chain = pending_hole.get();
             last_subseq = seg->subseq;
         }
 
         if( seg->shape == GR_SHAPE_LINE )
         {
             const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
+            const VECTOR2I      start( src->start_x, src->start_y );
+            const VECTOR2I      end( src->end_x, src->end_y );
 
-            if( zone_outline->VertexCount( 0, hole_idx ) == 0 )
-                zone_outline->Append( src->start_x, src->start_y, 0, hole_idx );
+            if( active_chain->PointCount() == 0 )
+            {
+                active_chain->Append( start );
+            }
+            else
+            {
+                const VECTOR2I& last = active_chain->CPoint( -1 );
 
-            zone_outline->Append( src->end_x, src->end_y, 0, hole_idx );
+                // Not if this can ever happen, or what do if it does (add both points?).
+                if( last != start )
+                {
+                    wxLogError( _( "Outline seems discontinuous: last point was %s, "
+                                   "start point of next segment is %s" ),
+                                last.Format(), start.Format() );
+                }
+            }
+
+            active_chain->Append( end );
         }
         else if( seg->shape == GR_SHAPE_ARC || seg->shape == GR_SHAPE_CIRCLE )
         {
             /* Even if it says "circle", it's actually an arc, it's just closed */
             const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
-            zone_outline->Hole( 0, hole_idx ).Append( src->result );
+            active_chain->Append( src->result );
         }
         else
         {
-            wxASSERT_MSG( false,
-                          wxString::Format( "Invalid shape type %d in zone outline", seg->shape ) );
+            wxLogError( _( "Invalid shape type %d in zone outline" ), seg->shape );
         }
     }
 
+    // Finalise the last hole, if any
+    add_hole_if_valid();
+
     if( zone_outline->Outline( 0 ).PointCount() >= 3 )
     {
         zone->SetOutline( zone_outline );