Don't print empty stack trace section in exception messages

When formatting an exception message with a stack trace requested, the
code unconditionally appended the preamble

    Stack trace (when copying this message, always include the lines below):

even when the stack trace string was empty. The resulting message ended
with a promise to "always include the lines below" and then nothing
below it, e.g.:

    Code: 49. DB::Exception: Backup checksum should not be zero (test.bin). (LOGICAL_ERROR), Stack trace (when copying this message, always include the lines below):

Only emit the "Stack trace" section when there are actually lines to
include, so the message never lies about including a stack trace. This
is applied consistently to the `DB::Exception`, `Poco::Exception`, and
`std::exception` formatting paths via a shared `formatStackTraceSection`
helper.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Alexey Milovidov
2026-06-05 05:07:36 +00:00
co-authored by Claude Opus 4.8
parent 9afcd1bfee
commit a11cdca398
+13 -3
View File
@@ -442,6 +442,16 @@ std::string getExtraExceptionInfo(const std::exception & e)
return msg;
}
/// Formats the trailing ", Stack trace (...)\n\n<trace>" section of an exception message.
/// Returns an empty string when there is no stack trace, so that the message never ends with the
/// "always include the lines below" promise without any lines following it.
static std::string formatStackTraceSection(const std::string & stack_trace)
{
if (stack_trace.empty())
return {};
return ", Stack trace (when copying this message, always include the lines below):\n\n" + stack_trace;
}
std::string getCurrentExceptionMessage(
bool with_stacktrace,
bool check_embedded_stacktrace /*= false*/,
@@ -483,7 +493,7 @@ PreformattedMessage getCurrentExceptionMessageAndPattern(
{
stream << "Poco::Exception. Code: " << ErrorCodes::POCO_EXCEPTION << ", e.code() = " << e.code()
<< ", " << e.displayText()
<< (with_stacktrace ? ", Stack trace (when copying this message, always include the lines below):\n\n" + getExceptionStackTraceString(e) : "")
<< (with_stacktrace ? formatStackTraceSection(getExceptionStackTraceString(e)) : "")
<< (with_extra_info ? getExtraExceptionInfo(e) : "");
if (with_version)
stream << " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
@@ -501,7 +511,7 @@ PreformattedMessage getCurrentExceptionMessageAndPattern(
name += " (demangling status: " + toString(status) + ")";
stream << "std::exception. Code: " << ErrorCodes::STD_EXCEPTION << ", type: " << name << ", e.what() = " << e.what()
<< (with_stacktrace ? ", Stack trace (when copying this message, always include the lines below):\n\n" + getExceptionStackTraceString(e) : "")
<< (with_stacktrace ? formatStackTraceSection(getExceptionStackTraceString(e)) : "")
<< (with_extra_info ? getExtraExceptionInfo(e) : "");
if (with_version)
stream << " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
@@ -662,7 +672,7 @@ PreformattedMessage getExceptionMessageAndPattern(const Exception & e, bool with
stream << " (" << ErrorCodes::getName(e.code()) << ")";
if (with_stacktrace && !has_embedded_stack_trace)
stream << ", Stack trace (when copying this message, always include the lines below):\n\n" << e.getStackTraceString();
stream << formatStackTraceSection(e.getStackTraceString());
}
catch (...) {} // NOLINT(bugprone-empty-catch) Ok: best-effort exception formatting, must not throw