diff --git a/src/core/Akka.Tests/Util/ResultSpec.cs b/src/core/Akka.Tests/Util/ResultSpec.cs
new file mode 100644
index 00000000000..8a77d3ddc98
--- /dev/null
+++ b/src/core/Akka.Tests/Util/ResultSpec.cs
@@ -0,0 +1,192 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (C) 2009-2024 Lightbend Inc.
+// Copyright (C) 2013-2024 .NET Foundation
+//
+// -----------------------------------------------------------------------
+
+using System;
+using System.Threading.Tasks;
+using Akka.Util;
+using FluentAssertions;
+using Xunit;
+using static FluentAssertions.FluentActions;
+
+namespace Akka.Tests.Util;
+
+public class ResultSpec
+{
+ [Fact(DisplayName = "Result constructor with value should return success")]
+ public void SuccessfulResult()
+ {
+ var result = new Result(1);
+
+ result.IsSuccess.Should().BeTrue();
+ result.Value.Should().Be(1);
+ result.Exception.Should().BeNull();
+ }
+
+ [Fact(DisplayName = "Result constructor with exception should return failed")]
+ public void ExceptionResult()
+ {
+ var result = new Result(new TestException("BOOM"));
+
+ result.IsSuccess.Should().BeFalse();
+ result.Exception.Should().NotBeNull();
+ result.Exception.Should().BeOfType();
+ }
+
+ [Fact(DisplayName = "Result.Success with value should return success")]
+ public void SuccessfulStaticSuccess()
+ {
+ var result = Result.Success(1);
+
+ result.IsSuccess.Should().BeTrue();
+ result.Value.Should().Be(1);
+ result.Exception.Should().BeNull();
+ }
+
+ [Fact(DisplayName = "Result.Failure with exception should return failed")]
+ public void ExceptionStaticFailure()
+ {
+ var result = Result.Failure(new TestException("BOOM"));
+
+ result.IsSuccess.Should().BeFalse();
+ result.Exception.Should().NotBeNull();
+ result.Exception.Should().BeOfType();
+ }
+
+ [Fact(DisplayName = "Result.From with successful Func should return success")]
+ public void SuccessfulFuncResult()
+ {
+ var result = Result.From(() => 1);
+
+ result.IsSuccess.Should().BeTrue();
+ result.Value.Should().Be(1);
+ result.Exception.Should().BeNull();
+ }
+
+ [Fact(DisplayName = "Result.From with throwing Func should return failed")]
+ public void ThrowFuncResult()
+ {
+ var result = Result.From(() => throw new TestException("BOOM"));
+
+ result.IsSuccess.Should().BeFalse();
+ result.Exception.Should().NotBeNull();
+ result.Exception.Should().BeOfType();
+ }
+
+ [Fact(DisplayName = "Result.FromTask with successful task should return success")]
+ public void SuccessfulTaskResult()
+ {
+ var task = CompletedTask(1);
+ var result = Result.FromTask(task);
+
+ result.IsSuccess.Should().BeTrue();
+ result.Value.Should().Be(1);
+ result.Exception.Should().BeNull();
+ }
+
+ [Fact(DisplayName = "Result.FromTask with faulted task should return failed")]
+ public void FaultedTaskResult()
+ {
+ var task = FaultedTask(1);
+ var result = Result.FromTask(task);
+
+ result.IsSuccess.Should().BeFalse();
+ result.Exception.Should().NotBeNull();
+ result.Exception.Should().BeOfType()
+ .Which.InnerException.Should().BeOfType();
+ }
+
+ [Fact(DisplayName = "Result.FromTask with cancelled task should return failed")]
+ public void CancelledTaskResult()
+ {
+ var task = CancelledTask(1);
+ var result = Result.FromTask(task);
+
+ result.IsSuccess.Should().BeFalse();
+ result.Exception.Should().NotBeNull();
+ result.Exception.Should().BeOfType();
+ }
+
+ [Fact(DisplayName = "Result.FromTask with incomplete task should throw")]
+ public void IncompleteTaskResult()
+ {
+ var tcs = new TaskCompletionSource();
+ Invoking(() => Result.FromTask(tcs.Task))
+ .Should().Throw().WithMessage("Task is not completed.*");
+ }
+
+ private static Task CompletedTask(int n)
+ {
+ var tcs = new TaskCompletionSource();
+ Task.Run(async () =>
+ {
+ await Task.Yield();
+ tcs.TrySetResult(n);
+ });
+ tcs.Task.Wait();
+ return tcs.Task;
+ }
+
+ private static Task CancelledTask(int n)
+ {
+ var tcs = new TaskCompletionSource();
+ Task.Run(async () =>
+ {
+ await Task.Yield();
+ tcs.TrySetCanceled();
+ });
+
+ try
+ {
+ tcs.Task.Wait();
+ }
+ catch
+ {
+ // no-op
+ }
+
+ return tcs.Task;
+ }
+
+ private static Task FaultedTask(int n)
+ {
+ var tcs = new TaskCompletionSource();
+ Task.Run(async () =>
+ {
+ await Task.Yield();
+ try
+ {
+ throw new TestException("BOOM");
+ }
+ catch (Exception ex)
+ {
+ tcs.TrySetException(ex);
+ }
+ });
+
+ try
+ {
+ tcs.Task.Wait();
+ }
+ catch
+ {
+ // no-op
+ }
+
+ return tcs.Task;
+ }
+
+ private class TestException: Exception
+ {
+ public TestException(string message) : base(message)
+ {
+ }
+
+ public TestException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/core/Akka/Util/Result.cs b/src/core/Akka/Util/Result.cs
index 5d9a4c02fb6..9969a0080b0 100644
--- a/src/core/Akka/Util/Result.cs
+++ b/src/core/Akka/Util/Result.cs
@@ -137,7 +137,30 @@ public static Result Failure(Exception exception)
/// TBD
public static Result FromTask(Task task)
{
- return task.IsCanceled || task.IsFaulted ? new Result(task.Exception) : new Result(task.Result);
+ if(!task.IsCompleted)
+ throw new ArgumentException("Task is not completed. Result.FromTask only accepts completed tasks.", nameof(task));
+
+ if(task.Exception is not null)
+ return new Result(task.Exception);
+
+ if (task.IsCanceled && task.Exception is null)
+ {
+ try
+ {
+ _ = task.GetAwaiter().GetResult();
+ }
+ catch(Exception e)
+ {
+ return new Result(e);
+ }
+
+ throw new InvalidOperationException("Should never reach this line!");
+ }
+
+ if(task.IsFaulted && task.Exception is null)
+ throw new InvalidOperationException("Should never happen! something is wrong with .NET Task code!");
+
+ return new Result(task.Result);
}
///