Repository: conan-io/conan

Issue:
Raise more meaningful messages from GraphInfo parsing
Changelog: omit
Docs: omit

Related to #4443, add more meaningful errors related to `graph_info.json` parsing. The output for the linked issue would have been:

```bash
⇒  conan info .
ERROR: Error parsing GraphInfo from file '/Users/jgsogo/dev/conan/issues/tmp2/graph_info.json': Error related to GraphInfo text content: Required node 'root' is not present
```

@lasote I'm not sure if I should open this PR against `develop` or against a minor `1.12.1`.

closes #4443


Discussion:
Develop. We try to keep the minor versions as lean as possible to fix the bugs there. AFAIK this is not a bug but an improvement. 

Resolving patch:
diff --git a/conans/model/graph_info.py b/conans/model/graph_info.py
--- a/conans/model/graph_info.py
+++ b/conans/model/graph_info.py
@@ -2,10 +2,11 @@
 import os
 
 from conans.client.profile_loader import _load_profile
+from conans.errors import ConanException
 from conans.model.options import OptionsValues
+from conans.model.ref import ConanFileReference
 from conans.tools import save
 from conans.util.files import load
-from conans.model.ref import ConanFileReference
 
 
 GRAPH_INFO_FILE = "graph_info.json"
@@ -24,7 +25,11 @@ def load(path):
         if not path:
             raise IOError("Invalid path")
         p = path if os.path.isfile(path) else os.path.join(path, GRAPH_INFO_FILE)
-        return GraphInfo.loads(load(p))
+        content = load(p)
+        try:
+            return GraphInfo.loads(content)
+        except Exception as e:
+            raise ConanException("Error parsing GraphInfo from file '{}': {}".format(p, e))
 
     @staticmethod
     def loads(text):
@@ -38,7 +43,7 @@ def loads(text):
             options = None
         else:
             options = OptionsValues(options)
-        root = graph_json["root"]
+        root = graph_json.get("root", {"name": None, "version": None, "user": None, "channel": None})
         root_ref = ConanFileReference(root["name"], root["version"], root["user"], root["channel"],
                                       validate=False)
         return GraphInfo(profile=profile, options=options, root_ref=root_ref)


Test patch:
diff --git a/conans/test/functional/command/info_test.py b/conans/test/functional/command/info_test.py
--- a/conans/test/functional/command/info_test.py
+++ b/conans/test/functional/command/info_test.py
@@ -9,6 +9,7 @@
 from conans.test.utils.cpp_test_files import cpp_hello_conan_files
 from conans.test.utils.tools import TestClient
 from conans.util.files import load, save
+from conans.test.utils.conanfile import TestConanFile
 
 
 class InfoTest(unittest.TestCase):
@@ -611,3 +612,20 @@ class MyTest(ConanFile):
         html_content = load(html_path)
         self.assertIn("<h3>Pkg/0.2@lasote/testing</h3>", html_content)
         self.assertIn("<li><b>topics</b>: foo", html_content)
+
+    def wrong_graph_info_test(self):
+        # https://github.com/conan-io/conan/issues/4443
+        conanfile = TestConanFile()
+        client = TestClient()
+        client.save({"conanfile.py": str(conanfile)})
+        client.run("install .")
+        path = os.path.join(client.current_folder, "graph_info.json")
+        graph_info = load(path)
+        graph_info = json.loads(graph_info)
+        graph_info.pop("root")
+        save(path, json.dumps(graph_info))
+        client.run("info .")
+        self.assertIn("conanfile.py (Hello/0.1@None/None)", client.out)
+        save(path, "broken thing")
+        client.run("info .", assert_error=True)
+        self.assertIn("ERROR: Error parsing GraphInfo from file", client.out)