Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 18988)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 18989)
@@ -20,7 +20,4 @@
 import java.util.stream.Collectors;
 
-import jakarta.json.Json;
-import jakarta.json.JsonObject;
-import jakarta.json.JsonReader;
 import javax.swing.ImageIcon;
 
@@ -39,4 +36,8 @@
 import org.openstreetmap.josm.tools.StreamUtils;
 import org.openstreetmap.josm.tools.Utils;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonReader;
 
 /**
@@ -205,4 +206,6 @@
         }
     }
+
+    private static final String[] EMPTY_STRING = new String[0];
 
     private double pixelPerDegree;
@@ -567,5 +570,5 @@
      * Check if this object equals another ImageryInfo with respect to the properties
      * that get written to the preference file.
-     *
+     * <p>
      * The field {@link #pixelPerDegree} is ignored.
      *
@@ -965,4 +968,47 @@
 
     /**
+     * Check to see if this info is valid (the XSD is overly permissive due to limitations of the XSD syntax).
+     * @return {@code true} if this info is valid
+     * @see ImageryInfo#getMissingFields()
+     * @since 18989
+     */
+    public boolean isValid() {
+        return this.getName() != null &&
+                this.getId() != null &&
+                this.getSourceType() != null &&
+                this.getUrl() != null &&
+                this.getImageryCategory() != null;
+    }
+
+    /**
+     * Get the missing fields for this info
+     * @return The missing fields, or an empty array
+     * @see ImageryInfo#isValid()
+     * @since 18989
+     */
+    public String[] getMissingFields() {
+        if (this.isValid()) {
+            return EMPTY_STRING;
+        }
+        List<String> missingFields = new ArrayList<>();
+        if (this.getName() == null) {
+            missingFields.add("name");
+        }
+        if (this.getId() == null) {
+            missingFields.add("id");
+        }
+        if (this.getSourceType() == null) {
+            missingFields.add("type");
+        }
+        if (this.getUrl() == null) {
+            missingFields.add("url");
+        }
+        if (this.getImageryCategory() == null) {
+            missingFields.add("category");
+        }
+        return missingFields.toArray(new String[0]);
+    }
+
+    /**
      * Return the sorted list of activated source IDs.
      * @return sorted list of activated source IDs
Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 18988)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 18989)
@@ -167,4 +167,6 @@
                 reader.setFastFail(fastFail);
                 Collection<ImageryInfo> result = reader.parse();
+                // See #23485 (remove invalid source entries)
+                result.removeIf(info -> !info.isValid());
                 newLayers.addAll(result);
             } catch (IOException ex) {
