From 5e5e6be259a0285f38e4403477fe9c7d3dbfa73e Mon Sep 17 00:00:00 2001 From: Chris Midgley Date: Mon, 25 Aug 2025 11:08:26 +0100 Subject: [PATCH] Add OriginalMessage property to CsvHelperException. This records the original message, without any extra details about row indexes etc. Fixes #2157. --- src/CsvHelper/CsvHelperException.cs | 22 ++++++++++++-- .../CsvHelperExceptionTests.cs | 29 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 tests/CsvHelper.Tests/CsvHelperExceptionTests.cs diff --git a/src/CsvHelper/CsvHelperException.cs b/src/CsvHelper/CsvHelperException.cs index fe8b90f3f..b4f0f514a 100644 --- a/src/CsvHelper/CsvHelperException.cs +++ b/src/CsvHelper/CsvHelperException.cs @@ -16,11 +16,19 @@ public class CsvHelperException : Exception [NonSerialized] private readonly CsvContext? context; + [NonSerialized] + private readonly string originalMessage = ""; + /// /// Gets the context. /// public CsvContext? Context => context; + /// + /// Gets the original error message, without added details. + /// + public string OriginalMessage => originalMessage; + /// /// Initializes a new instance of the CsvHelperException class. /// @@ -30,14 +38,20 @@ internal protected CsvHelperException() : base() { } /// Initializes a new instance of the CsvHelperException class. /// /// The message that describes the error. - internal protected CsvHelperException(string message) : base(message) { } + internal protected CsvHelperException(string message) : base(message) + { + originalMessage = message; + } /// /// Initializes a new instance of the CsvHelperException class. /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. - internal protected CsvHelperException(string message, Exception innerException) : base(message, innerException) { } + internal protected CsvHelperException(string message, Exception innerException) : base(message, innerException) + { + originalMessage = message; + } /// /// Initializes a new instance of the class. @@ -56,11 +70,12 @@ public CsvHelperException(CsvContext context) public CsvHelperException(CsvContext context, string message) : base(AddDetails(message, context)) { this.context = context; + originalMessage = message; } /// /// Initializes a new instance of the class - /// with a specified error message and a reference to the inner exception that + /// with a specified error message and a reference to the inner exception that /// is the cause of this exception. /// /// The context. @@ -69,6 +84,7 @@ public CsvHelperException(CsvContext context, string message) : base(AddDetails( public CsvHelperException(CsvContext context, string message, Exception innerException) : base(AddDetails(message, context), innerException) { this.context = context; + originalMessage = message; } private static string AddDetails(string message, CsvContext context) diff --git a/tests/CsvHelper.Tests/CsvHelperExceptionTests.cs b/tests/CsvHelper.Tests/CsvHelperExceptionTests.cs new file mode 100644 index 000000000..9fc1cf4cb --- /dev/null +++ b/tests/CsvHelper.Tests/CsvHelperExceptionTests.cs @@ -0,0 +1,29 @@ +using System.Globalization; +using CsvHelper.Configuration; +using CsvHelper.Tests.Mocks; +using Xunit; + +namespace CsvHelper.Tests; + +public class CsvHelperExceptionTests +{ + [Fact] + public void ExceptionStoresOriginalMessageInOriginalMessage() + { + var config = new CsvConfiguration(CultureInfo.InvariantCulture) + { + HasHeaderRecord = false, + }; + var parser = new ParserMock(config) + { + { "Id", "Name" } + }; + + var csv = new CsvReader(parser); + + var error = Assert.Throws(() => csv.ReadHeader()); + Assert.StartsWith("Configuration.HasHeaderRecord is false.", error.Message); + Assert.NotEqual("Configuration.HasHeaderRecord is false.", error.Message); + Assert.Equal("Configuration.HasHeaderRecord is false.", error.OriginalMessage); + } +}