Repository: conan-io/conan

Issue:
[bug] Consistent command results and error messages accross install/create and graph
### What is your suggestion?

When a dependency is `missing` or `invalid`, CLI commands `install`, `create`, etc, will output pertinent error messages and the exit code of the application will be `!=0`, meaning the command failed.

Making some tests, we have found an inconsistency all along `conan graph` subcommands.
`conan graph info` and `conan graph build-order` should output the same errors as `install` does and should fail in the same way. This does not imply when an error is found not generating the graph output. Commands should generate same graph output but they should fail.

## Example

```sh
 conan install --requires "unleash-client-cpp/1.3.0"
```

<details>

```

======== Input profiles ========
Profile host:
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.cppstd=gnu17
compiler.libcxx=libc++
compiler.version=15
os=Macos

Profile build:
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.cppstd=gnu17
compiler.libcxx=libc++
compiler.version=15
os=Macos


======== Computing dependency graph ========
Graph root
    cli
Requirements
    cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e - Cache
    libcurl/8.6.0#752cd9c3df0e9487b72301862f15a965 - Cache
    nlohmann_json/3.11.3#45828be26eb619a2e04ca517bb7b828d - Cache
    unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522 - Cache
    zlib/1.3.1#f52e03ae3d251dec704634230cd806a2 - Cache
Build requirements
    autoconf/2.71#f9307992909d7fb3df459340f1932809 - Cache
    automake/1.16.5#058bda3e21c36c9aa8425daf3c1faf50 - Cache
    gnu-config/cci.20210814#dc430d754f465e8c74463019672fb97b - Cache
    libtool/2.4.7#08316dad5c72c541ed21e039e4cf217b - Cache
    m4/1.4.19#b38ced39a01e31fef5435bc634461fd2 - Cache
    meson/1.2.2#04bdfb85d665c82b08a3510aee3ffd19 - Cache
    ninja/1.11.1#77587f8c8318662ac8e5a7867eb4be21 - Cache
    pkgconf/2.1.0#27f44583701117b571307cf5b5fe5605 - Cache
Resolved version ranges
    libcurl/[>=7.78.0 <9]: libcurl/8.6.0
    zlib/[>=1.2.11 <2]: zlib/1.3.1

======== Computing necessary packages ========
cpr/1.10.5: Compatible package ID 01269d344dd5584f4884dfbce8b9b77ae08480e6 equal to the default package ID: Skipping it.
cpr/1.10.5: Checking 5 compatible configurations
cpr/1.10.5: Compatible configurations not found in cache, checking servers
cpr/1.10.5: 'dfee4b06bfe93e4207413fffd653ec0bb2ef0d5c': compiler.cppstd=17
cpr/1.10.5: '1b581912b6a996992095ec289d8a10caf974e2df': compiler.cppstd=20
cpr/1.10.5: 'b59d6a6f26a2d7f87370035527ef219122bb9093': compiler.cppstd=gnu20
cpr/1.10.5: '7b4224207f3ce01b81f5d00add4296667a22e2ba': compiler.cppstd=23
cpr/1.10.5: '839343c67d23729138e2ac0c4c4386e281cd1ffc': compiler.cppstd=gnu23
unleash-client-cpp/1.3.0: Compatible package ID e73661f3afc6efaa3f8ba17b9263163c204f8c52 equal to the default package ID: Skipping it.
unleash-client-cpp/1.3.0: Checking 5 compatible configurations
unleash-client-cpp/1.3.0: Compatible configurations not found in cache, checking servers
unleash-client-cpp/1.3.0: '6332c366c6c529367a2b716abd77f657b96d3c47': compiler.cppstd=17
unleash-client-cpp/1.3.0: '9194788030461f5d772c319ed4b3d745e75e2d47': compiler.cppstd=20
unleash-client-cpp/1.3.0: 'ee3dc95e09fa0d284e807bdf988a10a5cc6e59c9': compiler.cppstd=gnu20
unleash-client-cpp/1.3.0: '4ff0d71ebb4d6a0900ca4e36b2b780168848c458': compiler.cppstd=23
unleash-client-cpp/1.3.0: 'dafb9907f23cc61560a25535b18340c225627896': compiler.cppstd=gnu23
Requirements
    cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e:01269d344dd5584f4884dfbce8b9b77ae08480e6 - Missing
    libcurl/8.6.0#752cd9c3df0e9487b72301862f15a965:267b943cf33985f9448f72c97e42fcdb5094fcf5 - Missing
    unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522:e73661f3afc6efaa3f8ba17b9263163c204f8c52 - Missing
    zlib/1.3.1#f52e03ae3d251dec704634230cd806a2:2ee39e692ca4177b4b689b15bc1f2cfdf8f83706 - Missing
Build requirements
Skipped binaries
    nlohmann_json/3.11.3, autoconf/2.71, automake/1.16.5, gnu-config/cci.20210814, libtool/2.4.7, m4/1.4.19, meson/1.2.2, ninja/1.11.1, pkgconf/2.1.0
ERROR: Missing binary: cpr/1.10.5:01269d344dd5584f4884dfbce8b9b77ae08480e6
ERROR: Missing binary: libcurl/8.6.0:267b943cf33985f9448f72c97e42fcdb5094fcf5
ERROR: Missing binary: unleash-client-cpp/1.3.0:e73661f3afc6efaa3f8ba17b9263163c204f8c52
ERROR: Missing binary: zlib/1.3.1:2ee39e692ca4177b4b689b15bc1f2cfdf8f83706

zlib/1.3.1: WARN: Can't find a 'zlib/1.3.1' package binary '2ee39e692ca4177b4b689b15bc1f2cfdf8f83706' for the configuration:
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.version=15
os=Macos
[options]
fPIC=True
shared=False

ERROR: Missing prebuilt package for 'cpr/1.10.5', 'libcurl/8.6.0', 'unleash-client-cpp/1.3.0', 'zlib/1.3.1'. You can try:
    - List all available packages using 'conan list zlib/1.3.1:* -r=remote'
    - Explain missing binaries: replace 'conan install ...' with 'conan graph explain ...'
    - Try to build locally from sources using the '--build=cpr/1.10.5 --build=libcurl/8.6.0 --build=unleash-client-cpp/1.3.0 --build=zlib/1.3.1' argument

More Info at 'https://docs.conan.io/2/knowledge/faq.html#error-missing-prebuilt-package'

```
</details>

```sh
conan graph info --requires "unleash-client-cpp/1.3.0" > /dev/null
```

<details>

```
======== Computing dependency graph ========
Graph root
    cli
Requirements
    cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e - Cache
    libcurl/8.6.0#752cd9c3df0e9487b72301862f15a965 - Cache
    nlohmann_json/3.11.3#45828be26eb619a2e04ca517bb7b828d - Cache
    unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522 - Cache
    zlib/1.3.1#f52e03ae3d251dec704634230cd806a2 - Cache
Build requirements
    autoconf/2.71#f9307992909d7fb3df459340f1932809 - Cache
    automake/1.16.5#058bda3e21c36c9aa8425daf3c1faf50 - Cache
    gnu-config/cci.20210814#dc430d754f465e8c74463019672fb97b - Cache
    libtool/2.4.7#08316dad5c72c541ed21e039e4cf217b - Cache
    m4/1.4.19#b38ced39a01e31fef5435bc634461fd2 - Cache
    meson/1.2.2#04bdfb85d665c82b08a3510aee3ffd19 - Cache
    ninja/1.11.1#77587f8c8318662ac8e5a7867eb4be21 - Cache
    pkgconf/2.1.0#27f44583701117b571307cf5b5fe5605 - Cache
Resolved version ranges
    libcurl/[>=7.78.0 <9]: libcurl/8.6.0
    zlib/[>=1.2.11 <2]: zlib/1.3.1

======== Computing necessary packages ========
cpr/1.10.5: Compatible package ID 01269d344dd5584f4884dfbce8b9b77ae08480e6 equal to the default package ID: Skipping it.
cpr/1.10.5: Checking 5 compatible configurations
cpr/1.10.5: Compatible configurations not found in cache, checking servers
cpr/1.10.5: 'dfee4b06bfe93e4207413fffd653ec0bb2ef0d5c': compiler.cppstd=17
cpr/1.10.5: '1b581912b6a996992095ec289d8a10caf974e2df': compiler.cppstd=20
cpr/1.10.5: 'b59d6a6f26a2d7f87370035527ef219122bb9093': compiler.cppstd=gnu20
cpr/1.10.5: '7b4224207f3ce01b81f5d00add4296667a22e2ba': compiler.cppstd=23
cpr/1.10.5: '839343c67d23729138e2ac0c4c4386e281cd1ffc': compiler.cppstd=gnu23
unleash-client-cpp/1.3.0: Compatible package ID e73661f3afc6efaa3f8ba17b9263163c204f8c52 equal to the default package ID: Skipping it.
unleash-client-cpp/1.3.0: Checking 5 compatible configurations
unleash-client-cpp/1.3.0: Compatible configurations not found in cache, checking servers
unleash-client-cpp/1.3.0: '6332c366c6c529367a2b716abd77f657b96d3c47': compiler.cppstd=17
unleash-client-cpp/1.3.0: '9194788030461f5d772c319ed4b3d745e75e2d47': compiler.cppstd=20
unleash-client-cpp/1.3.0: 'ee3dc95e09fa0d284e807bdf988a10a5cc6e59c9': compiler.cppstd=gnu20
unleash-client-cpp/1.3.0: '4ff0d71ebb4d6a0900ca4e36b2b780168848c458': compiler.cppstd=23
unleash-client-cpp/1.3.0: 'dafb9907f23cc61560a25535b18340c225627896': compiler.cppstd=gnu23
Requirements
    cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e:01269d344dd5584f4884dfbce8b9b77ae08480e6 - Missing
    libcurl/8.6.0#752cd9c3df0e9487b72301862f15a965:267b943cf33985f9448f72c97e42fcdb5094fcf5 - Missing
    unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522:e73661f3afc6efaa3f8ba17b9263163c204f8c52 - Missing
    zlib/1.3.1#f52e03ae3d251dec704634230cd806a2:2ee39e692ca4177b4b689b15bc1f2cfdf8f83706 - Missing
Build requirements
Skipped binaries
    nlohmann_json/3.11.3, autoconf/2.71, automake/1.16.5, gnu-config/cci.20210814, libtool/2.4.7, m4/1.4.19, meson/1.2.2, ninja/1.11.1, pkgconf/2.1.0

-------- Basic graph information --------
```
</details>

```sh
conan graph build-order --order-by=recipe --requires "unleash-client-cpp/1.3.0" > /dev/null
```

<details>

```

======== Computing dependency graph ========
Graph root
    cli
Requirements
    cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e - Cache
    libcurl/8.8.0#9170903d66d6f6e6795fbb566a403129 - Cache
    nlohmann_json/3.11.3#45828be26eb619a2e04ca517bb7b828d - Cache
    unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522 - Cache
    zlib/1.3.1#f52e03ae3d251dec704634230cd806a2 - Cache
Build requirements
    autoconf/2.71#f9307992909d7fb3df459340f1932809 - Cache
    automake/1.16.5#058bda3e21c36c9aa8425daf3c1faf50 - Cache
    gnu-config/cci.20210814#dc430d754f465e8c74463019672fb97b - Cache
    libtool/2.4.7#08316dad5c72c541ed21e039e4cf217b - Cache
    m4/1.4.19#b38ced39a01e31fef5435bc634461fd2 - Cache
    meson/1.2.2#04bdfb85d665c82b08a3510aee3ffd19 - Cache
    ninja/1.11.1#77587f8c8318662ac8e5a7867eb4be21 - Cache
    pkgconf/2.1.0#27f44583701117b571307cf5b5fe5605 - Cache
Resolved version ranges
    libcurl/[>=7.78.0 <9]: libcurl/8.8.0
    zlib/[>=1.2.11 <2]: zlib/1.3.1

======== Computing necessary packages ========
cpr/1.10.5: Compatible package ID 6da29aec5517b05e82cc36e7f0cea25b756592a1 equal to the default package ID: Skipping it.
cpr/1.10.5: Checking 5 compatible configurations
cpr/1.10.5: Compatible configurations not found in cache, checking servers
cpr/1.10.5: '7c482b127e0f60686b2846e9000db88c8e11666f': compiler.cppstd=17
cpr/1.10.5: '8190a0a48706aaa6e73601f80ccad90dfb634831': compiler.cppstd=20
cpr/1.10.5: '033edebf48b9a193f4775cf79a226f42000d28f2': compiler.cppstd=gnu20
cpr/1.10.5: '4e3c016827f1e0890c20a8270f1157bc59399749': compiler.cppstd=23
cpr/1.10.5: 'a938cbada2ed4cf609e1017660a982a2d845dfe8': compiler.cppstd=gnu23
unleash-client-cpp/1.3.0: Compatible package ID 31826f3ddae6dc65d0a7c1f89aee57b356b02c35 equal to the default package ID: Skipping it.
unleash-client-cpp/1.3.0: Checking 5 compatible configurations
unleash-client-cpp/1.3.0: Compatible configurations not found in cache, checking servers
unleash-client-cpp/1.3.0: '075884ad7d4ee0b37c12ff00532b073d1d95a6ed': compiler.cppstd=17
unleash-client-cpp/1.3.0: 'd1b62f23ae496268defae946797d055b11f8ad07': compiler.cppstd=20
unleash-client-cpp/1.3.0: '1d89d0c5293777c413984d998ab350e8c256ff3f': compiler.cppstd=gnu20
unleash-client-cpp/1.3.0: '8dbe067233297ceac6a97a9cfdc6f0511d43e42d': compiler.cppstd=23
unleash-client-cpp/1.3.0: '0d0154f2b3f41e640700de62e84302ebd7f3ad54': compiler.cppstd=gnu23
Requirements
    cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e:6da29aec5517b05e82cc36e7f0cea25b756592a1 - Missing
    libcurl/8.8.0#9170903d66d6f6e6795fbb566a403129:da91ff7ebbce6962ceee923568a6793fa42e7ebe - Missing
    unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522:31826f3ddae6dc65d0a7c1f89aee57b356b02c35 - Missing
    zlib/1.3.1#f52e03ae3d251dec704634230cd806a2:2ee39e692ca4177b4b689b15bc1f2cfdf8f83706 - Missing
Build requirements
Skipped binaries
    nlohmann_json/3.11.3, autoconf/2.71, automake/1.16.5, gnu-config/cci.20210814, libtool/2.4.7, m4/1.4.19, meson/1.2.2, ninja/1.11.1, pkgconf/2.1.0

======== Computing the build order ========
zlib/1.3.1#f52e03ae3d251dec704634230cd806a2:2ee39e692ca4177b4b689b15bc1f2cfdf8f83706 - Missing
libcurl/8.8.0#9170903d66d6f6e6795fbb566a403129:da91ff7ebbce6962ceee923568a6793fa42e7ebe - Missing
cpr/1.10.5#a227f2a4f0deed3ee1f8b8237477129e:6da29aec5517b05e82cc36e7f0cea25b756592a1 - Missing
unleash-client-cpp/1.3.0#a4354a616cd5bdb81354825229954522:31826f3ddae6dc65d0a7c1f89aee57b356b02c35 - Missing
```
</details>

As it can be easily seen, the graph info and build-order commands does not output the expected errors.

### Have you read the CONTRIBUTING guide?

- [X] I've read the CONTRIBUTING guide


Discussion:

Patch:
diff --git a/conan/cli/command.py b/conan/cli/command.py
index 7a074dc7cef..b2b50f1c7ab 100644
--- a/conan/cli/command.py
+++ b/conan/cli/command.py
@@ -98,6 +98,12 @@ def _format(self, parser, info, *args):
 
         formatter(info)
 
+    def _dispatch_errors(self, info):
+        if info and isinstance(info, dict):
+            if info.get("conan_error"):
+                raise ConanException(info["conan_error"])
+            if info.get("conan_warning"):
+                ConanOutput().warning(info["conan_warning"])
 
 class ConanArgumentParser(argparse.ArgumentParser):
 
@@ -179,6 +185,7 @@ def run(self, conan_api, *args):
             else:
                 sub.set_parser(subcommand_parser, conan_api)
                 sub.run(conan_api, parser, *args)
+        self._dispatch_errors(info)
 
     @property
     def group(self):
@@ -198,6 +205,7 @@ def run(self, conan_api, parent_parser, *args):
         info = self._method(conan_api, parent_parser, self._parser, *args)
         # It is necessary to do it after calling the "method" otherwise parser not complete
         self._format(parent_parser, info, *args)
+        self._dispatch_errors(info)
 
     def set_name(self, parent_name):
         self._name = self._subcommand_name.replace(f'{parent_name}-', '', 1)
diff --git a/conan/cli/commands/graph.py b/conan/cli/commands/graph.py
index 8f412474438..690be886985 100644
--- a/conan/cli/commands/graph.py
+++ b/conan/cli/commands/graph.py
@@ -45,8 +45,9 @@ def graph(conan_api, parser, *args):
     """
 
 
-def cli_build_order(build_order):
+def cli_build_order(result):
     # TODO: Very simple cli output, probably needs to be improved
+    build_order = result["build_order"]
     build_order = build_order["order"] if isinstance(build_order, dict) else build_order
     for level in build_order:
         for item in level:
@@ -59,8 +60,8 @@ def cli_build_order(build_order):
                 cli_out_write(f"{item['ref']}:{item['package_id']} - {item['binary']}")
 
 
-def json_build_order(build_order):
-    cli_out_write(json.dumps(build_order, indent=4))
+def json_build_order(result):
+    cli_out_write(json.dumps(result["build_order"], indent=4))
 
 
 @conan_subcommand(formatters={"text": cli_build_order, "json": json_build_order,
@@ -128,7 +129,9 @@ def graph_build_order(conan_api, parser, subparser, *args):
                                                   clean=args.lockfile_clean)
     conan_api.lockfile.save_lockfile(lockfile, args.lockfile_out, cwd)
 
-    return install_order_serialized
+    return {"build_order": install_order_serialized,
+            "conan_error": install_graph.get_errors()}
+
 
 
 @conan_subcommand(formatters={"text": cli_build_order, "json": json_build_order,
@@ -159,7 +162,9 @@ def graph_build_order_merge(conan_api, parser, subparser, *args):
     install_order_serialized = result.install_build_order()
     if getattr(result, "legacy"):
         install_order_serialized = install_order_serialized["order"]
-    return install_order_serialized
+
+    return {"build_order": install_order_serialized,
+            "conan_error": result.get_errors()}
 
 
 @conan_subcommand(formatters={"text": format_graph_info,
@@ -237,7 +242,10 @@ def graph_info(conan_api, parser, subparser, *args):
     return {"graph": deps_graph,
             "field_filter": args.filter,
             "package_filter": args.package_filter,
-            "conan_api": conan_api}
+            "conan_api": conan_api,
+            "conan_error": str(deps_graph.error) if deps_graph.error else None,
+            # Do not compute graph errors if there are dependency errors
+            "conan_warning": InstallGraph(deps_graph).get_errors() if not deps_graph.error else None}
 
 
 @conan_subcommand(formatters={"text": explain_formatter_text,
diff --git a/conan/cli/formatters/graph/build_order_html.py b/conan/cli/formatters/graph/build_order_html.py
index e785bb01bdd..07d6af46eeb 100644
--- a/conan/cli/formatters/graph/build_order_html.py
+++ b/conan/cli/formatters/graph/build_order_html.py
@@ -266,7 +266,8 @@ def _render_build_order(build_order, template):
     return template.render(context)
 
 
-def format_build_order_html(build_order):
+def format_build_order_html(result):
+    build_order = result["build_order"]
     build_order = build_order["order"] if isinstance(build_order, dict) else build_order
     template = Template(build_order_html, autoescape=select_autoescape(['html', 'xml']))
     cli_out_write(_render_build_order(build_order, template))
diff --git a/conan/cli/formatters/graph/graph.py b/conan/cli/formatters/graph/graph.py
index 5ebfd23dd9b..7bec8ba658d 100644
--- a/conan/cli/formatters/graph/graph.py
+++ b/conan/cli/formatters/graph/graph.py
@@ -126,8 +126,6 @@ def format_graph_html(result):
         error["type"] = "conflict"
         error["should_highlight_node"] = lambda node: node.id == graph.error.node.id
     cli_out_write(_render_graph(graph, error, template, template_folder))
-    if graph.error:
-        raise graph.error
 
 
 def format_graph_dot(result):
@@ -138,8 +136,6 @@ def format_graph_dot(result):
     user_template = os.path.join(template_folder, "graph.dot")
     template = load(user_template) if os.path.isfile(user_template) else graph_info_dot
     cli_out_write(_render_graph(graph, None, template, template_folder))
-    if graph.error:
-        raise graph.error
 
 
 def format_graph_json(result):
@@ -150,5 +146,3 @@ def format_graph_json(result):
     serial = filter_graph(serial, package_filter=package_filter, field_filter=field_filter)
     json_result = json.dumps({"graph": serial}, indent=4)
     cli_out_write(json_result)
-    if graph.error:
-        raise graph.error
diff --git a/conan/cli/formatters/graph/graph_info_text.py b/conan/cli/formatters/graph/graph_info_text.py
index 055496f54b1..aa431cd8573 100644
--- a/conan/cli/formatters/graph/graph_info_text.py
+++ b/conan/cli/formatters/graph/graph_info_text.py
@@ -33,8 +33,6 @@ def format_graph_info(result):
     for n in serial["nodes"].values():
         cli_out_write(f"{n['ref']}:")  # FIXME: This can be empty for consumers and it is ugly ":"
         _serial_pretty_printer(n, indent="  ")
-    if graph.error:
-        raise graph.error
 
 
 def _serial_pretty_printer(data, indent=""):
diff --git a/conans/client/graph/install_graph.py b/conans/client/graph/install_graph.py
index 65befe30de1..312ba57e71c 100644
--- a/conans/client/graph/install_graph.py
+++ b/conans/client/graph/install_graph.py
@@ -454,27 +454,62 @@ def install_build_order(self):
                   "order": [[n.serialize() for n in level] for level in install_order]}
         return result
 
-    def raise_errors(self):
+    def _get_missing_invalid_packages(self):
         missing, invalid = [], []
-        for ref, install_node in self._nodes.items():
-            for package in install_node.packages.values():
-                if package.binary == BINARY_MISSING:
-                    missing.append(package)
-                elif package.binary == BINARY_INVALID:
-                    invalid.append(package)
+        def analyze_package(package):
+            if package.binary == BINARY_MISSING:
+                missing.append(package)
+            elif package.binary == BINARY_INVALID:
+                invalid.append(package)
+        for _, install_node in self._nodes.items():
+            if self._order == "recipe":
+                for package in install_node.packages.values():
+                    analyze_package(package)
+            elif self._order == "configuration":
+                analyze_package(install_node)
+
+        return missing, invalid
 
+    def raise_errors(self):
+        missing, invalid = self._get_missing_invalid_packages()
+        if invalid:
+            self._raise_invalid(invalid)
+        if missing:
+            self._raise_missing(missing)
+
+    def get_errors(self):
+        missing, invalid = self._get_missing_invalid_packages()
+        errors = []
+        tab = "    "
+        if invalid or missing:
+            errors.append("There are some error(s) in the graph:")
         if invalid:
-            msg = ["There are invalid packages:"]
             for package in invalid:
                 node = package.nodes[0]
                 if node.cant_build and node.should_build:
                     binary, reason = "Cannot build for this configuration", node.cant_build
                 else:
                     binary, reason = "Invalid", node.conanfile.info.invalid
-                msg.append("{}: {}: {}".format(node.conanfile, binary, reason))
-            raise ConanInvalidConfiguration("\n".join(msg))
+                errors.append(f"{tab}- {node.ref}: {binary}: {reason}")
         if missing:
-            self._raise_missing(missing)
+            missing_prefs = set(n.nodes[0].pref for n in missing)  # avoid duplicated
+            for pref in list(sorted([str(pref) for pref in missing_prefs])):
+                errors.append(f"{tab}- {pref}: Missing binary")
+        if errors:
+            return "\n".join(errors)
+        return None
+
+    def _raise_invalid(self, invalid):
+        msg = ["There are invalid packages:"]
+        for package in invalid:
+            node = package.nodes[0]
+            if node.cant_build and node.should_build:
+                binary, reason = "Cannot build for this configuration", node.cant_build
+            else:
+                binary, reason = "Invalid", node.conanfile.info.invalid
+            msg.append("{}: {}: {}".format(node.conanfile, binary, reason))
+        raise ConanInvalidConfiguration("\n".join(msg))
+
 
     def _raise_missing(self, missing):
         # TODO: Remove out argument


Test patch:
diff --git a/test/integration/command_v2/test_info_build_order.py b/test/integration/command_v2/test_info_build_order.py
index b735920e185..24ea1642733 100644
--- a/test/integration/command_v2/test_info_build_order.py
+++ b/test/integration/command_v2/test_info_build_order.py
@@ -76,9 +76,9 @@ def test_info_build_order():
     # test html format
     c.run("graph build-order consumer --build=missing --format=html")
     assert "<body>" in c.stdout
-    c.run("graph build-order consumer --order-by=recipe --format=html")
+    c.run("graph build-order consumer --order-by=recipe --build=missing --format=html")
     assert "<body>" in c.stdout
-    c.run("graph build-order consumer --order-by=configuration --format=html")
+    c.run("graph build-order consumer --order-by=configuration --build=missing --format=html")
     assert "<body>" in c.stdout
 
 
@@ -540,6 +540,58 @@ def test_info_build_order_lockfile_location():
     assert os.path.exists(os.path.join(c.current_folder, "myconan2.lock"))
 
 
+def test_build_order_missing_package_check_error():
+    c = TestClient()
+    c.save({"dep/conanfile.py": GenConanfile(),
+            "pkg/conanfile.py": GenConanfile().with_requires("dep/0.1"),
+            "consumer/conanfile.txt": "[requires]\npkg/0.1"})
+    c.run("export dep --name=dep --version=0.1")
+    c.run("export pkg --name=pkg --version=0.1")
+
+    exit_code = c.run("graph build-order consumer --build='pkg/*' --order=configuration --format=json", assert_error=True)
+    bo_json = json.loads(c.stdout)
+    assert bo_json["order_by"] == "configuration"
+    assert exit_code != 0
+    assert "dep/0.1:da39a3ee5e6b4b0d3255bfef95601890afd80709: Missing binary" in c.out
+
+    result = [
+        [
+            {
+                "ref": "dep/0.1#4d670581ccb765839f2239cc8dff8fbd",
+                "pref": "dep/0.1#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709",
+                "package_id": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+                "prev": None,
+                "context": "host",
+                "binary": "Missing",
+                "options": [],
+                "filenames": [],
+                "depends": [],
+                "overrides": {},
+                "build_args": None,
+            }
+        ],
+        [
+            {
+                "ref": "pkg/0.1#1ac8dd17c0f9f420935abd3b6a8fa032",
+                "pref": "pkg/0.1#1ac8dd17c0f9f420935abd3b6a8fa032:59205ba5b14b8f4ebc216a6c51a89553021e82c1",
+                "package_id": "59205ba5b14b8f4ebc216a6c51a89553021e82c1",
+                "prev": None,
+                "context": "host",
+                "binary": "Build",
+                "options": [],
+                "filenames": [],
+                "depends": [
+                    "dep/0.1#4d670581ccb765839f2239cc8dff8fbd:da39a3ee5e6b4b0d3255bfef95601890afd80709"
+                ],
+                "overrides": {},
+                "build_args": "--requires=pkg/0.1 --build=pkg/0.1",
+            }
+        ],
+    ]
+
+    assert bo_json["order"] == result
+
+
 def test_info_build_order_broken_recipe():
     # https://github.com/conan-io/conan/issues/14104
     c = TestClient()