Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions src/Wolfgang.TryPattern/Try.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static Result Run([NotNull] Action? action)
}
catch (Exception ex)
{
return Result.Failure(ex.Message);
return Result.Failure(SafeErrorMessage(ex));
}
}

Expand Down Expand Up @@ -82,7 +82,7 @@ public static Result Run([NotNull] Action? action)
}
catch (Exception ex)
{
return Result<T?>.Failure(ex.Message);
return Result<T?>.Failure(SafeErrorMessage(ex));
}
}
#else
Expand All @@ -99,7 +99,7 @@ public static Result<T> Run<T>([NotNull] Func<T>? function)
}
catch (Exception ex)
{
return Result<T>.Failure(ex.Message);
return Result<T>.Failure(SafeErrorMessage(ex));
}
}
#endif
Expand Down Expand Up @@ -160,7 +160,7 @@ public static async Task<Result> RunAsync([NotNull] Action? action, Cancellation
}
catch (Exception ex)
{
return Result.Failure(ex.Message);
return Result.Failure(SafeErrorMessage(ex));
}
}

Expand Down Expand Up @@ -215,7 +215,7 @@ public static async Task<Result> RunAsync([NotNull] Action? action, Cancellation
}
catch (Exception ex)
{
return Result<T?>.Failure(ex.Message);
return Result<T?>.Failure(SafeErrorMessage(ex));
}
}
#else
Expand Down Expand Up @@ -243,8 +243,23 @@ public static async Task<Result<T>> RunAsync<T>([NotNull] Func<Task<T>>? functio
}
catch (Exception ex)
{
return Result<T>.Failure(ex.Message);
return Result<T>.Failure(SafeErrorMessage(ex));
}
}
#endif



// Coerce an exception message that Result.Failure would reject
// (null / empty / whitespace) into a usable fallback so Try.Run
// itself never throws from inside its catch block. Fixes #273:
// consumers of exceptions with whitespace-only Message (or the
// parameterless-ctor variants of some framework exceptions) were
// getting an ArgumentException out of Try.Run rather than a
// Failed Result.
private static string SafeErrorMessage(Exception ex)
{
string message = ex.Message;
return string.IsNullOrWhiteSpace(message) ? ex.GetType().Name : message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Regression tests for #273 — Try.Run must not throw when the caught
// exception's Message is null / empty / whitespace. Before the fix,
// these calls raised ArgumentException from inside Result.Failure
// (which rejects whitespace) and defeated the whole point of the Try
// wrapper. After the fix, Try.Run falls back to the exception's type
// name so callers always get a Failed Result.

using System;
using System.Threading.Tasks;

namespace Wolfgang.TryPattern.Tests.Unit;

public class TryRunWhitespaceMessageTests
{
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("\t")]
[InlineData("\n")]
public void Run_Action_when_exception_message_is_whitespace_returns_Failed(string message)
{
Result r = Try.Run(() => throw new InvalidOperationException(message));

Assert.True(r.Failed);
Assert.Equal(nameof(InvalidOperationException), r.ErrorMessage);
}


[Theory]
[InlineData("")]
[InlineData(" ")]
public void Run_Generic_when_exception_message_is_whitespace_returns_Failed(string message)
{
Result<int> r = Try.Run<int>(() => throw new InvalidOperationException(message));

Assert.True(r.Failed);
Assert.Equal(nameof(InvalidOperationException), r.ErrorMessage);
}


[Theory]
[InlineData("")]
[InlineData(" ")]
public async Task RunAsync_Action_when_exception_message_is_whitespace_returns_Failed(string message)
{
Result r = await Try.RunAsync(() => throw new InvalidOperationException(message));

Assert.True(r.Failed);
Assert.Equal(nameof(InvalidOperationException), r.ErrorMessage);
}


[Theory]
[InlineData("")]
[InlineData(" ")]
public async Task RunAsync_Generic_when_exception_message_is_whitespace_returns_Failed(string message)
{
Result<int> r = await Try.RunAsync<int>(() => throw new InvalidOperationException(message));

Assert.True(r.Failed);
Assert.Equal(nameof(InvalidOperationException), r.ErrorMessage);
}


[Fact]
public void Run_Action_when_exception_message_is_non_whitespace_preserves_it()
{
// Regression guard: the fallback path must not kick in when
// the exception carries a real message.
Result r = Try.Run(() => throw new InvalidOperationException("real message"));

Assert.True(r.Failed);
Assert.Equal("real message", r.ErrorMessage);
}
}