diff --git a/src/bin/ch-remote.rs b/src/bin/ch-remote.rs index f0c9b5e09..4ef0b0257 100644 --- a/src/bin/ch-remote.rs +++ b/src/bin/ch-remote.rs @@ -1131,8 +1131,8 @@ fn main() { } }; - if let Err(e) = target_api.do_command(&matches) { - eprintln!("Error running command: {e}"); + if let Err(top_error) = target_api.do_command(&matches) { + cloud_hypervisor::cli_print_error_chain(&top_error, "ch-remote"); process::exit(1) }; } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..ec941ea52 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,27 @@ +// Copyright © 2025 Cyberus Technology GmbH +// +// SPDX-License-Identifier: Apache-2.0 +// + +/// Prints a chain of errors to the user in a consistent manner. +/// The user will see a clear chain of errors, followed by debug output +/// for opening issues. +pub fn cli_print_error_chain(top_error: &dyn std::error::Error, component: &str) { + eprint!("Error: {component} exited with the following "); + if top_error.source().is_none() { + eprintln!("error:"); + eprintln!(" {top_error}"); + } else { + eprintln!("chain of errors:"); + std::iter::successors(Some(top_error), |sub_error| { + sub_error.source() + }) + .enumerate() + .for_each(|(level, error)| { + eprintln!(" {level}: {error}",); + }); + } + + eprintln!(); + eprintln!("Debug Info: {top_error:?}"); +} diff --git a/src/main.rs b/src/main.rs index 0520dcf7c..924cb19c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -883,8 +883,8 @@ fn main() { path.map(|s| std::fs::remove_file(s).ok()); 0 } - Err(e) => { - eprintln!("{e}"); + Err(top_error) => { + cloud_hypervisor::cli_print_error_chain(&top_error, "Cloud Hypervisor"); 1 } }; diff --git a/vmm/src/api/http/mod.rs b/vmm/src/api/http/mod.rs index 3493a3134..9b2f5d1d8 100644 --- a/vmm/src/api/http/mod.rs +++ b/vmm/src/api/http/mod.rs @@ -69,9 +69,20 @@ pub enum HttpError { const HTTP_ROOT: &str = "/api/v1"; +/// Creates the error response's body meant to be sent back to an API client. +/// The error message contained in the response is supposed to be user-facing, +/// thus insightful and helpful while balancing technical accuracy and +/// simplicity. pub fn error_response(error: HttpError, status: StatusCode) -> Response { let mut response = Response::new(Version::Http11, status); - response.set_body(Body::new(format!("{error}"))); + // We must use debug output here without `#`, as it is currently the only + // feasible option to get all relevant error details to the receiver, + // i.e., ch-remote, in a balanced form. The Display impl is not guaranteed + // to hold all relevant or helpful data. + // + // TODO: We might print a nice error chain here as well and send it to the + // remote, similar to the normal error reporting? + response.set_body(Body::new(format!("{error:?}"))); response }