SOLR-14506: Fix NPE in COLSTATUS for collections using implicit routing.
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 84502c8..b53fee9 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -402,6 +402,8 @@
 
 * SOLR-15482: Cross-collection join fixed to ignore documents that do not contain value in "from" field (hossman)
 
+* SOLR-14506: Fix NPE in COLSTATUS for collections using implicit routing. (Austin Weidler, ab)
+
 Other Changes
 ---------------------
 * SOLR-15566: Clarify ref guide documentation about SQL queries with `SELECT *` requiring a `LIMIT` clause (Timothy Potter)
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java b/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
index 91f04d0..e679f97 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ColStatus.java
@@ -76,7 +76,7 @@
     Collection<String> collections;
     String col = props.getStr(ZkStateReader.COLLECTION_PROP);
     if (col == null) {
-      collections = new HashSet<>(clusterState.getCollectionsMap().keySet());
+      collections = new HashSet<>(clusterState.getCollectionStates().keySet());
     } else {
       collections = Collections.singleton(col);
     }
@@ -148,7 +148,9 @@
         replicaMap.add("recovering", recoveringReplicas);
         replicaMap.add("recovery_failed", recoveryFailedReplicas);
         sliceMap.add("state", s.getState().toString());
-        sliceMap.add("range", s.getRange().toString());
+        if (s.getRange() != null) {
+          sliceMap.add("range", s.getRange().toString());
+        }
         Map<String, RoutingRule> rules = s.getRoutingRules();
         if (rules != null && !rules.isEmpty()) {
           sliceMap.add("routingRules", rules);
@@ -169,6 +171,9 @@
           continue;
         }
         String url = ZkCoreNodeProps.getCoreUrl(leader);
+        if (url == null) {
+          continue;
+        }
         try (SolrClient client = solrClientCache.getHttpSolrClient(url)) {
           ModifiableSolrParams params = new ModifiableSolrParams();
           params.add(CommonParams.QT, "/admin/segments");
diff --git a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
index 13baa60..0011ea0 100644
--- a/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/CollectionsAPISolrJTest.java
@@ -668,6 +668,20 @@
     assertEquals(0, rsp.getStatus());
     Number down = (Number) rsp.getResponse().findRecursive(collectionName, "shards", "shard1", "replicas", "down");
     assertTrue("should be some down replicas, but there were none in shard1:" + rsp, down.intValue() > 0);
+
+    // test for a collection with implicit router
+    String implicitColl = "implicitColl";
+    CollectionAdminRequest.createCollection(implicitColl, "conf2", 2, 1)
+        .setRouterName("implicit")
+        .setRouterField("routerField")
+        .setShards("shardA,shardB")
+        .process(cluster.getSolrClient());
+    cluster.waitForActiveCollection(implicitColl, 2, 2);
+    req = CollectionAdminRequest.collectionStatus(implicitColl);
+    rsp = req.process(cluster.getSolrClient());
+    assertNotNull(rsp.getResponse().get(implicitColl));
+    assertNotNull(rsp.toString(), rsp.getResponse().findRecursive(implicitColl, "shards", "shardA"));
+    assertNotNull(rsp.toString(), rsp.getResponse().findRecursive(implicitColl, "shards", "shardB"));
   }
   
   @Test