Repository: conan-io/conan

Issue:
[bug] conan graph explain when there are no binaries to compare to
### Environment details

* Operating System+version:
* Compiler+version:
* Conan version:
* Python version:


### Steps to reproduce

1. start from an empty Conan cache
2. make a change to any recipe and export it to the local cache
3. try to do a `conan graph explain --require=xxx` against the exported recipe (or any other recipe that depends on the exported one)





### Logs

Output I get:
```
======== Closest binaries ========
freetype/2.13.2
  freetype/2.13.2#dfa3d504cae4a08d5c72113bd6f28498%1709732954.153072 (2024-03-06 13:49:14 UTC)
```

This revision (which was _just_ exported) does not exist in any remote, and has _no_ binaries (not even locally) - I think the output could be friendlier, perhaps: "there are no existing binary packages to compare against" or something along those lines


Discussion:

Patch:
diff --git a/conan/api/subapi/list.py b/conan/api/subapi/list.py
index f748de9ac48..1e92cf74d98 100644
--- a/conan/api/subapi/list.py
+++ b/conan/api/subapi/list.py
@@ -223,7 +223,6 @@ def explain_missing_binaries(self, ref, conaninfo, remotes):
                 ConanOutput().info(f"Finding binaries in remote {remote.name}")
                 pkg_configurations = self.packages_configurations(ref, remote=remote)
             except Exception as e:
-                pass
                 ConanOutput(f"ERROR IN REMOTE {remote.name}: {e}")
             else:
                 candidates.extend(_BinaryDistance(pref, data, conaninfo, remote)
@@ -232,8 +231,7 @@ def explain_missing_binaries(self, ref, conaninfo, remotes):
         candidates.sort()
         pkglist = PackagesList()
         pkglist.add_refs([ref])
-        # If there are exact matches, only return the matches
-        # else, limit to the number specified
+        # Return the closest matches, stop adding when distance is increased
         candidate_distance = None
         for candidate in candidates:
             if candidate_distance and candidate.distance != candidate_distance:
diff --git a/conan/cli/commands/graph.py b/conan/cli/commands/graph.py
index b8fb4a36bda..b672d2cccab 100644
--- a/conan/cli/commands/graph.py
+++ b/conan/cli/commands/graph.py
@@ -22,6 +22,11 @@ def explain_formatter_text(data):
         # we need to wrap this in a MultiPackagesList
         pkglist = data["closest_binaries"]
         prepare_pkglist_compact(pkglist)
+        # Now we make sure that if there are no binaries we will print something that makes sense
+        for ref, ref_info in pkglist.items():
+            for rrev, rrev_info in ref_info.items():
+                if not rrev_info:
+                    rrev_info["ERROR"] = "No package binaries exist"
         print_serial(pkglist)
 
 


Test patch:
diff --git a/conans/test/integration/command_v2/test_graph_find_binaries.py b/conans/test/integration/command_v2/test_graph_find_binaries.py
index 5c92ee87a78..385d4e4efa5 100644
--- a/conans/test/integration/command_v2/test_graph_find_binaries.py
+++ b/conans/test/integration/command_v2/test_graph_find_binaries.py
@@ -454,3 +454,13 @@ def test_multiple_distance_ordering(self):
         assert "a657a8fc96dd855e2a1c90a9fe80125f0c4635a0" not in tc.out
         # We expect the closer binary to show
         assert "a6923b987deb1469815dda84aab36ac34f370c48" in tc.out
+
+
+def test_no_binaries():
+    # https://github.com/conan-io/conan/issues/15819
+    c = TestClient()
+    c.save({"conanfile.py": GenConanfile("pkg", "0.1")})
+    c.run("export .")
+    c.run("graph explain --requires=pkg/0.1")
+    assert "ERROR: No package binaries exist" in c.out
+    # The json is not an issue, it won't have anything as contents