diff --git a/test/boost/aws_errors_test.cc b/test/boost/aws_errors_test.cc index 1190d1e100..6f5d595372 100644 --- a/test/boost/aws_errors_test.cc +++ b/test/boost/aws_errors_test.cc @@ -108,3 +108,57 @@ BOOST_AUTO_TEST_CASE(TestErrorsWithoutPrefixParse) { BOOST_REQUIRE_EQUAL("JunkMessage", error.get_error_message()); BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no); } + +BOOST_AUTO_TEST_CASE(TestNestedException) { + // Test nested exceptions where the innermost is a system_error + try { + try { + try { + throw std::system_error(std::error_code(ECONNABORTED, std::system_category())); + } catch (...) { + std::throw_with_nested(std::runtime_error("Higher level runtime_error")); + } + } catch (...) { + std::throw_with_nested(std::logic_error("Higher level logic_error")); + } + } catch (...) { + auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception()); + BOOST_REQUIRE_EQUAL(aws::aws_error_type::NETWORK_CONNECTION, error.get_error_type()); + BOOST_REQUIRE_EQUAL("Software caused connection abort", error.get_error_message()); + BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::yes); + } + + // Test nested exceptions where the innermost is NOT a system_error + try { + try { + throw std::logic_error("Something bad happened"); + } catch (...) { + std::throw_with_nested(std::runtime_error("Higher level runtime_error")); + } + } catch (...) { + auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception()); + BOOST_REQUIRE_EQUAL(aws::aws_error_type::UNKNOWN, error.get_error_type()); + BOOST_REQUIRE_EQUAL("Higher level runtime_error", error.get_error_message()); + BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no); + } + + // Test single exception which is NOT a nested exception + try { + throw std::runtime_error("Something bad happened"); + } catch (...) { + auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception()); + BOOST_REQUIRE_EQUAL(aws::aws_error_type::UNKNOWN, error.get_error_type()); + BOOST_REQUIRE_EQUAL("Something bad happened", error.get_error_message()); + BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no); + } + + // Test with non-std::exception + try { + throw "foo"; + } catch (...) { + auto error = aws::aws_error::from_maybe_nested_exception(std::current_exception()); + BOOST_REQUIRE_EQUAL(aws::aws_error_type::UNKNOWN, error.get_error_type()); + BOOST_REQUIRE_EQUAL("No error message was provided, exception content: char const*", error.get_error_message()); + BOOST_REQUIRE_EQUAL(error.is_retryable(), aws::retryable::no); + } +} diff --git a/utils/s3/aws_error.cc b/utils/s3/aws_error.cc index caa971ae48..45f87f1bf9 100644 --- a/utils/s3/aws_error.cc +++ b/utils/s3/aws_error.cc @@ -160,24 +160,41 @@ aws_error aws_error::from_system_error(const std::system_error& system_error) { } } -aws_error aws_error::from_maybe_nested_exception(const std::exception& maybe_nested_error) { - const std::exception* current_exception = &maybe_nested_error; - while (current_exception) { - if (auto sys_error = dynamic_cast(current_exception)) { - return from_system_error(*sys_error); - } +aws_error aws_error::from_maybe_nested_exception(std::exception_ptr eptr) { + std::string original_message; + while (eptr) { try { - std::rethrow_if_nested(*current_exception); - } catch (const std::exception& inner) { - current_exception = &inner; - continue; + std::rethrow_exception(eptr); + } catch (const std::exception& e) { + if (original_message.empty()) { + original_message = e.what(); + } + + if (auto* sys = dynamic_cast(&e)) { + return from_system_error(*sys); + } + + try { + std::rethrow_if_nested(e); + } catch (...) { + eptr = std::current_exception(); + continue; + } + break; } catch (...) { + // Non-std::exception, should not happen in general break; } - current_exception = nullptr; } - return {aws_error_type::UNKNOWN, maybe_nested_error.what(), retryable::no}; + + if (original_message.empty()) { + original_message = fmt::format("No error message was provided, exception content: {}", eptr); + } + + return {aws_error_type::UNKNOWN, std::move(original_message), retryable::no}; } + + aws_error aws_error::from_exception_ptr(std::exception_ptr exception) { if (exception) { try { @@ -188,8 +205,8 @@ aws_error aws_error::from_exception_ptr(std::exception_ptr exception) { return from_http_code(ex.status()); } catch (const std::system_error& ex) { return from_system_error(ex); - } catch (const std::exception& ex) { - return from_maybe_nested_exception(ex); + } catch (const std::exception&) { + return from_maybe_nested_exception(std::current_exception()); } catch (...) { return aws_error{aws_error_type::UNKNOWN, seastar::format("{}", std::current_exception()), retryable::no}; } diff --git a/utils/s3/aws_error.hh b/utils/s3/aws_error.hh index 3dc7830371..76b3475b80 100644 --- a/utils/s3/aws_error.hh +++ b/utils/s3/aws_error.hh @@ -106,7 +106,7 @@ public: static std::optional parse(seastar::sstring&& body); static aws_error from_http_code(seastar::http::reply::status_type http_code); static aws_error from_system_error(const std::system_error& system_error); - static aws_error from_maybe_nested_exception(const std::exception& maybe_nested_error); + static aws_error from_maybe_nested_exception(std::exception_ptr maybe_nested_error); static aws_error from_exception_ptr(std::exception_ptr exception); static const aws_errors& get_errors(); };