From c1488673aa3a22bd87a6af080d8272cab678ec8f Mon Sep 17 00:00:00 2001
From: Adam Simon <adamosimoni@gmail.com>
Date: Wed, 15 Nov 2023 09:29:15 +0100
Subject: [PATCH] Improve error handling consistency in prerequisite flag
 evaluation

---
 .../ConfigV2EvaluationTests.cs                | 107 +++++++++++++-----
 .../EvaluationLogTests.cs                     |  14 +--
 .../Helpers/LoggingHelper.cs                  |   4 +-
 .../circular_dependency_override.json         |  28 -----
 .../evaluationlog/circular_dependency.json    |  14 ---
 .../circular_dependency.txt                   |  21 ----
 .../data/test_circulardependency_v6.json      |  48 ++++----
 .../Evaluation/RolloutEvaluator.cs            |  32 +++---
 src/ConfigCatClient/Logging/LogMessages.cs    |   5 -
 9 files changed, 119 insertions(+), 154 deletions(-)
 delete mode 100644 src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json
 delete mode 100644 src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json
 delete mode 100644 src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt

diff --git a/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs b/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs
index ce63e563..aa3f8adf 100644
--- a/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs
+++ b/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs
@@ -222,49 +222,96 @@ public void UnicodeMatrixTests(string configLocation, string settingKey, string
             userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue);
     }
 
-    [TestMethod]
-    public void CircularDependencyTest()
+    [DataTestMethod]
+    [DataRow("key1", "'key1' -> 'key1'")]
+    [DataRow("key2", "'key2' -> 'key3' -> 'key2'")]
+    [DataRow("key4", "'key4' -> 'key3' -> 'key2' -> 'key3'")]
+    public void PrerequisiteFlagCircularDependencyTest(string key, string dependencyCycle)
     {
         var config = new ConfigLocation.LocalFile("data", "test_circulardependency_v6.json").FetchConfig();
 
-        var logEvents = new List<LogEvent>();
-        var logger = LoggingHelper.CreateCapturingLogger(logEvents);
-
+        var logger = new Mock<IConfigCatLogger>().Object.AsWrapper();
         var evaluator = new RolloutEvaluator(logger);
 
-        const string key = "key1";
-        var evaluationDetails = evaluator.Evaluate<object?>(config!.Settings, key, defaultValue: null, user: null, remoteConfig: null, logger);
-
-        Assert.AreEqual(4, logEvents.Count);
+        var ex = Assert.ThrowsException<InvalidOperationException>(() => evaluator.Evaluate<object?>(config!.Settings, key, defaultValue: null, user: null, remoteConfig: null, logger));
 
-        Assert.AreEqual(3, logEvents.Count(evt => evt.EventId == 3005));
+        StringAssert.Contains(ex.Message, "Circular dependency detected");
+        StringAssert.Contains(ex.Message, dependencyCycle);
+    }
 
-        Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Warning
-            && (string?)evt.Message.ArgValues[1] == "key1"
-            && (string?)evt.Message.ArgValues[2] == "'key1' -> 'key1'"));
+    [DataTestMethod]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", true, "Dog")]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", false, "Cat")]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", "1", null)]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", 1, null)]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", 1.0, null)]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", new[] { true }, null)]
+    [DataRow("stringDependsOnBool", "mainBoolFlag", null, null)]
+    [DataRow("stringDependsOnString", "mainStringFlag", "private", "Dog")]
+    [DataRow("stringDependsOnString", "mainStringFlag", "Private", "Cat")]
+    [DataRow("stringDependsOnString", "mainStringFlag", true, null)]
+    [DataRow("stringDependsOnString", "mainStringFlag", 1, null)]
+    [DataRow("stringDependsOnString", "mainStringFlag", 1.0, null)]
+    [DataRow("stringDependsOnString", "mainStringFlag", new[] { "private" }, null)]
+    [DataRow("stringDependsOnString", "mainStringFlag", null, null)]
+    [DataRow("stringDependsOnInt", "mainIntFlag", 2, "Dog")]
+    [DataRow("stringDependsOnInt", "mainIntFlag", 1, "Cat")]
+    [DataRow("stringDependsOnInt", "mainIntFlag", "2", null)]
+    [DataRow("stringDependsOnInt", "mainIntFlag", true, null)]
+    [DataRow("stringDependsOnInt", "mainIntFlag", 2.0, null)]
+    [DataRow("stringDependsOnInt", "mainIntFlag", new[] { 2 }, null)]
+    [DataRow("stringDependsOnInt", "mainIntFlag", null, null)]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", 0.1, "Dog")]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", 0.11, "Cat")]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", "0.1", null)]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", true, null)]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", 1, null)]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", new[] { 0.1 }, null)]
+    [DataRow("stringDependsOnDouble", "mainDoubleFlag", null, null)]
+    public async Task PrerequisiteFlagComparisonValueTypeMismatchTest(string key, string prerequisiteFlagKey, object? prerequisiteFlagValue, object? expectedValue)
+    {
+        var cdnLocation = (ConfigLocation.Cdn)new FlagDependencyMatrixTestsDescriptor().ConfigLocation;
 
-        Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Warning
-            && (string?)evt.Message.ArgValues[1] == "key2"
-            && (string?)evt.Message.ArgValues[2] == "'key1' -> 'key2' -> 'key1'"));
+        var logEvents = new List<LogEvent>();
+        var logger = LoggingHelper.CreateCapturingLogger(logEvents);
 
-        Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Warning
-            && (string?)evt.Message.ArgValues[1] == "key3"
-            && (string?)evt.Message.ArgValues[2] == "'key1' -> 'key3' -> 'key3'"));
+        var overrideDictionary = new Dictionary<string, object> { [prerequisiteFlagKey] = prerequisiteFlagValue! };
 
-        var evaluateLogEvent = logEvents.FirstOrDefault(evt => evt.Level == LogLevel.Info && evt.EventId == 5000);
-        Assert.IsNotNull(evaluateLogEvent);
+        var options = new ConfigCatClientOptions
+        {
+            FlagOverrides = FlagOverrides.LocalDictionary(overrideDictionary, OverrideBehaviour.LocalOverRemote),
+            PollingMode = PollingModes.ManualPoll,
+            Logger = logger
+        };
+        cdnLocation.ConfigureBaseUrl(options);
 
-        StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex(
-            "THEN 'key1-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine
-            + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage)));
+        using var client = new ConfigCatClient(cdnLocation.SdkKey, options);
+        await client.ForceRefreshAsync();
 
-        StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex(
-            "THEN 'key2-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine
-            + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage)));
+        var actualValue = await client.GetValueAsync(key, (object?)null);
+        Assert.AreEqual(expectedValue, actualValue);
 
-        StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex(
-            "THEN 'key3-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine
-            + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage)));
+        if (expectedValue is null)
+        {
+            var errors = logEvents.Where(evt => evt.Level == LogLevel.Error).ToArray();
+            Assert.AreEqual(1, errors.Length);
+            Assert.AreEqual(1002, errors[0].EventId);
+            var ex = errors[0].Exception;
+            Assert.IsInstanceOfType(ex, typeof(InvalidOperationException));
+
+            if (prerequisiteFlagValue == null)
+            {
+                StringAssert.Contains(ex!.Message, "Setting value is null");
+            }
+            else if (prerequisiteFlagValue.GetType().ToSettingType() == Setting.UnknownType)
+            {
+                StringAssert.Matches(ex!.Message, new Regex("Setting value '[^']+' is of an unsupported type"));
+            }
+            else
+            {
+                StringAssert.Matches(ex!.Message, new Regex("Type mismatch between comparison value '[^']+' and prerequisite flag '[^']+'"));
+            }
+        }
     }
 
     [DataTestMethod]
diff --git a/src/ConfigCat.Client.Tests/EvaluationLogTests.cs b/src/ConfigCat.Client.Tests/EvaluationLogTests.cs
index 54f6faf2..87852101 100644
--- a/src/ConfigCat.Client.Tests/EvaluationLogTests.cs
+++ b/src/ConfigCat.Client.Tests/EvaluationLogTests.cs
@@ -136,16 +136,6 @@ public void ComparatorsTests(string testSetName, string? sdkKey, string? baseUrl
         RunTest(testSetName, sdkKey, baseUrlOrOverrideFileName, key, defaultValue, userObject, expectedReturnValue, expectedLogFileName);
     }
 
-    private static IEnumerable<object?[]> GetPrerequisiteFlagConditionsWithCircularDependencyTests() => GetTests("circular_dependency");
-
-    [DataTestMethod]
-    [DynamicData(nameof(GetPrerequisiteFlagConditionsWithCircularDependencyTests), DynamicDataSourceType.Method)]
-    public void PrerequisiteFlagConditionsWithCircularDependencyTests(string testSetName, string? sdkKey, string? baseUrlOrOverrideFileName,
-        string key, string? defaultValue, string userObject, string? expectedReturnValue, string expectedLogFileName)
-    {
-        RunTest(testSetName, sdkKey, baseUrlOrOverrideFileName, key, defaultValue, userObject, expectedReturnValue, expectedLogFileName);
-    }
-
     private static IEnumerable<object?[]> GetEpochDateValidationTests() => GetTests("epoch_date_validation");
 
     [DataTestMethod]
@@ -243,7 +233,7 @@ private static void RunTest(string testSetName, string? sdkKey, string? baseUrlO
         }
 
         var logEvents = new List<LogEvent>();
-        var logger = LoggingHelper.CreateCapturingLogger(logEvents);
+        var logger = LoggingHelper.CreateCapturingLogger(logEvents).AsWrapper();
 
         ConfigLocation configLocation = sdkKey is { Length: > 0 }
             ? new ConfigLocation.Cdn(sdkKey, baseUrlOrOverrideFileName)
@@ -313,7 +303,7 @@ public void EvaluationLogShouldBeBuiltOnlyWhenNecessary(LogLevel logLevel, bool
         var settings = new ConfigLocation.Cdn("configcat-sdk-1/PKDVCLf-Hq-h-kCzMp-L7Q/AG6C1ngVb0CvM07un6JisQ").FetchConfigCached().Settings;
 
         var logEvents = new List<LogEvent>();
-        var logger = LoggingHelper.CreateCapturingLogger(logEvents, logLevel);
+        var logger = LoggingHelper.CreateCapturingLogger(logEvents, logLevel).AsWrapper();
 
         var evaluator = new RolloutEvaluator(logger);
 
diff --git a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs
index 3cfef7aa..1ed18ede 100644
--- a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs
+++ b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs
@@ -24,7 +24,7 @@ public static LoggerWrapper AsWrapper(this IConfigCatLogger logger, Hooks? hooks
         return new LoggerWrapper(logger, hooks);
     }
 
-    public static LoggerWrapper CreateCapturingLogger(List<LogEvent> logEvents, LogLevel logLevel = LogLevel.Info)
+    public static IConfigCatLogger CreateCapturingLogger(List<LogEvent> logEvents, LogLevel logLevel = LogLevel.Info)
     {
         var loggerMock = new Mock<IConfigCatLogger>();
 
@@ -33,6 +33,6 @@ public static LoggerWrapper CreateCapturingLogger(List<LogEvent> logEvents, LogL
         loggerMock.Setup(logger => logger.Log(It.IsAny<LogLevel>(), It.IsAny<LogEventId>(), ref It.Ref<FormattableLogMessage>.IsAny, It.IsAny<Exception>()))
             .Callback(delegate (LogLevel level, LogEventId eventId, ref FormattableLogMessage msg, Exception ex) { logEvents.Add(new LogEvent(level, eventId, ref msg, ex)); });
 
-        return loggerMock.Object.AsWrapper();
+        return loggerMock.Object;
     }
 }
diff --git a/src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json b/src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json
deleted file mode 100644
index 0e9d9d95..00000000
--- a/src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "p": {
-    "u": "https://cdn-global.configcat.com",
-    "r": 0
-  },
-  "f": {
-    "key1": {
-      "t": 1,
-      "v": { "s": "value1" },
-      "r": [
-        {"c": [{"p": {"f": "key2", "c": 0, "v": {"s": "fourth"}}}], "s": {"v": {"s": "first"}}},
-        {"c": [{"p": {"f": "key3", "c": 0, "v": {"s": "value3"}}}], "s": {"v": {"s": "second"}}}
-      ]
-    },
-    "key2": {
-      "t": 1,
-      "v": { "s": "value2" },
-      "r": [
-        {"c": [{"p": {"f": "key1", "c": 0, "v": {"s": "value1"}}}], "s": {"v": {"s": "third"}}},
-        {"c": [{"p": {"f": "key3", "c": 0, "v": {"s": "value3"}}}], "s": {"v": {"s": "fourth"}}}
-      ]
-    },
-    "key3": {
-      "t": 1,
-      "v": { "s": "value3" }
-    }
-  }
-}
diff --git a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json b/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json
deleted file mode 100644
index cb50d22f..00000000
--- a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "jsonOverride": "circular_dependency_override.json",
-  "tests": [
-    {
-      "key": "key1",
-      "defaultValue": "default",
-      "user": {
-        "Identifier": "1234"
-      },
-      "returnValue": "first",
-      "expectedLog": "circular_dependency.txt"
-    }
-  ]
-}
diff --git a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt b/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt
deleted file mode 100644
index 3dcbcb0f..00000000
--- a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-WARNING [3005] Cannot evaluate condition (Flag 'key1' EQUALS 'value1') for setting 'key2' (circular dependency detected between the following depending flags: 'key1' -> 'key2' -> 'key1'). Please check your feature flag definition and eliminate the circular dependency.
-INFO [5000] Evaluating 'key1' for User '{"Identifier":"1234"}'
-  Evaluating targeting rules and applying the first match if any:
-  - IF Flag 'key2' EQUALS 'fourth'
-    (
-      Evaluating prerequisite flag 'key2':
-      Evaluating targeting rules and applying the first match if any:
-      - IF Flag 'key1' EQUALS 'value1' THEN 'third' => cannot evaluate, circular dependency detected
-        The current targeting rule is ignored and the evaluation continues with the next rule.
-      - IF Flag 'key3' EQUALS 'value3'
-        (
-          Evaluating prerequisite flag 'key3':
-          Prerequisite flag evaluation result: 'value3'.
-          Condition (Flag 'key3' EQUALS 'value3') evaluates to true.
-        )
-        THEN 'fourth' => MATCH, applying rule
-      Prerequisite flag evaluation result: 'fourth'.
-      Condition (Flag 'key2' EQUALS 'fourth') evaluates to true.
-    )
-    THEN 'first' => MATCH, applying rule
-  Returning 'first'.
diff --git a/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json b/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json
index d8a7d047..a8a9e176 100644
--- a/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json
+++ b/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json
@@ -6,7 +6,7 @@
   "f": {
     "key1": {
       "t": 1,
-      "v": { "s": "value1" },
+      "v": { "s": "key1-value" },
       "r": [
         {
           "c": [
@@ -14,59 +14,53 @@
               "p": {
                 "f": "key1",
                 "c": 0,
-                "v": { "s": "key1-prereq1" }
+                "v": { "s": "key1-prereq" }
               }
             }
           ],
-          "s": { "v": { "s": "key1-prereq1" } }
-        },
-        {
-          "c": [
-            {
-              "p": {
-                "f": "key2",
-                "c": 0,
-                "v": { "s": "key1-prereq2" }
-              }
-            }
-          ],
-          "s": { "v": { "s": "key1-prereq2" } }
-        },
+          "s": { "v": { "s": "key1-prereq" } }
+        }
+      ]
+    },
+    "key2": {
+      "t": 1,
+      "v": { "s": "key2-value" },
+      "r": [
         {
           "c": [
             {
               "p": {
                 "f": "key3",
                 "c": 0,
-                "v": { "s": "key1-prereq3" }
+                "v": { "s": "key3-prereq" }
               }
             }
           ],
-          "s": { "v": { "s": "key1-prereq3" } }
+          "s": { "v": { "s": "key2-prereq" } }
         }
       ]
     },
-    "key2": {
+    "key3": {
       "t": 1,
-      "v": { "s": "value2" },
+      "v": { "s": "key3-value" },
       "r": [
         {
           "c": [
             {
               "p": {
-                "f": "key1",
+                "f": "key2",
                 "c": 0,
-                "v": { "s": "key2-prereq1" }
+                "v": { "s": "key2-prereq" }
               }
             }
           ],
-          "s": { "v": { "s": "key2-prereq1" } }
+          "s": { "v": { "s": "key3-prereq" } }
         }
       ]
     },
-    "key3": {
+    "key4": {
       "t": 1,
-      "v": { "s": "value3" },
+      "v": { "s": "key4-value" },
       "r": [
         {
           "c": [
@@ -74,11 +68,11 @@
               "p": {
                 "f": "key3",
                 "c": 0,
-                "v": { "s": "key3-prereq1" }
+                "v": { "s": "key3-prereq" }
               }
             }
           ],
-          "s": { "v": { "s": "key3-prereq1" } }
+          "s": { "v": { "s": "key4-prereq" } }
         }
       ]
     }
diff --git a/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs b/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs
index 8784fdba..a86eeed4 100644
--- a/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs
+++ b/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs
@@ -13,7 +13,6 @@ internal sealed class RolloutEvaluator : IRolloutEvaluator
     internal const string MissingUserObjectError = "cannot evaluate, User Object is missing";
     internal const string MissingUserAttributeError = "cannot evaluate, the User.{0} attribute is missing";
     internal const string InvalidUserAttributeError = "cannot evaluate, the User.{0} attribute is invalid ({1})";
-    internal const string CircularDependencyError = "cannot evaluate, circular dependency detected";
 
     internal const string TargetingRuleIgnoredMessage = "The current targeting rule is ignored and the evaluation continues with the next rule.";
 
@@ -46,7 +45,7 @@ public EvaluateResult Evaluate<T>(T defaultValue, ref EvaluateContext context, [
         returnValue = default!;
         try
         {
-            var result = EvaluateSetting(ref context);
+            EvaluateResult result;
 
             if (typeof(T) != typeof(object))
             {
@@ -64,13 +63,18 @@ public EvaluateResult Evaluate<T>(T defaultValue, ref EvaluateContext context, [
                     throw new InvalidOperationException(
                         "The type of a setting must match the type of the specified default value. "
                         + $"Setting's type was {context.Setting.SettingType} but the default value's type was {typeof(T)}. "
-                        + $"Please use a default value which corresponds to the setting type {context.Setting.SettingType}.");
+                        + $"Please use a default value which corresponds to the setting type {context.Setting.SettingType}. "
+                        + "Learn more: https://configcat.com/docs/sdk-reference/dotnet/#setting-type-mapping");
                 }
 
+                result = EvaluateSetting(ref context);
+
                 returnValue = result.Value.GetValue<T>(expectedSettingType)!;
             }
             else
             {
+                result = EvaluateSetting(ref context);
+
                 returnValue = (T)(context.Setting.SettingType != Setting.UnknownType
                     ? result.Value.GetValue(context.Setting.SettingType)!
                     : result.Value.GetValue()!);
@@ -281,7 +285,7 @@ private bool EvaluateConditions<TCondition>(TCondition[] conditions, TargetingRu
 
                 case PrerequisiteFlagCondition prerequisiteFlagCondition:
                     conditionResult = EvaluatePrerequisiteFlagCondition(prerequisiteFlagCondition, ref context, out error);
-                    newLineBeforeThen = error is null || error != CircularDependencyError || conditions.Length > 1;
+                    newLineBeforeThen = true;
                     break;
 
                 case SegmentCondition segmentCondition:
@@ -721,10 +725,12 @@ private bool EvaluatePrerequisiteFlagCondition(PrerequisiteFlagCondition conditi
             throw new InvalidOperationException("Prerequisite flag key is missing or invalid.");
         }
 
-        var comparisonValue = condition.ComparisonValue.GetValue(throwIfInvalid: false);
-        if (comparisonValue is null || comparisonValue.GetType().ToSettingType() != prerequisiteFlag.SettingType)
+        var comparisonValue = EnsureComparisonValue(condition.ComparisonValue.GetValue(throwIfInvalid: false));
+
+        var expectedSettingType = comparisonValue.GetType().ToSettingType();
+        if (prerequisiteFlag.SettingType != Setting.UnknownType && prerequisiteFlag.SettingType != expectedSettingType)
         {
-            EnsureComparisonValue<string>(null);
+            throw new InvalidOperationException($"Type mismatch between comparison value '{comparisonValue}' and prerequisite flag '{prerequisiteFlagKey}'.");
         }
 
         context.VisitedFlags.Add(context.Key);
@@ -732,11 +738,7 @@ private bool EvaluatePrerequisiteFlagCondition(PrerequisiteFlagCondition conditi
         {
             context.VisitedFlags.Add(prerequisiteFlagKey!);
             var dependencyCycle = new StringListFormatter(context.VisitedFlags).ToString("a", CultureInfo.InvariantCulture);
-            this.logger.CircularDependencyDetected(condition.ToString(), context.Key, dependencyCycle);
-
-            context.VisitedFlags.RemoveRange(context.VisitedFlags.Count - 2, 2);
-            error = CircularDependencyError;
-            return false;
+            throw new InvalidOperationException($"Circular dependency detected between the following depending flags: {dependencyCycle}.");
         }
 
         var prerequisiteFlagContext = new EvaluateContext(prerequisiteFlagKey!, prerequisiteFlag!, ref context);
@@ -750,13 +752,13 @@ private bool EvaluatePrerequisiteFlagCondition(PrerequisiteFlagCondition conditi
 
         context.VisitedFlags.RemoveAt(context.VisitedFlags.Count - 1);
 
-        var prerequisiteFlagValue = prerequisiteFlagEvaluateResult.Value.GetValue(prerequisiteFlag!.SettingType, throwIfInvalid: false);
+        var prerequisiteFlagValue = prerequisiteFlagEvaluateResult.Value.GetValue(expectedSettingType, throwIfInvalid: true)!;
 
         var comparator = condition.Comparator;
         var result = comparator switch
         {
-            PrerequisiteFlagComparator.Equals => prerequisiteFlagValue is not null && prerequisiteFlagValue.Equals(comparisonValue),
-            PrerequisiteFlagComparator.NotEquals => prerequisiteFlagValue is not null && !prerequisiteFlagValue.Equals(comparisonValue),
+            PrerequisiteFlagComparator.Equals => prerequisiteFlagValue.Equals(comparisonValue),
+            PrerequisiteFlagComparator.NotEquals => !prerequisiteFlagValue.Equals(comparisonValue),
             _ => throw new InvalidOperationException("Comparison operator is invalid.")
         };
 
diff --git a/src/ConfigCatClient/Logging/LogMessages.cs b/src/ConfigCatClient/Logging/LogMessages.cs
index 34de3d30..193e1239 100644
--- a/src/ConfigCatClient/Logging/LogMessages.cs
+++ b/src/ConfigCatClient/Logging/LogMessages.cs
@@ -136,11 +136,6 @@ public static FormattableLogMessage UserObjectAttributeIsInvalid(this LoggerWrap
         $"Cannot evaluate condition ({condition}) for setting '{key}' ({reason}). Please check the User.{attributeName} attribute and make sure that its value corresponds to the comparison operator.",
         "CONDITION", "KEY", "REASON", "ATTRIBUTE_NAME");
 
-    public static FormattableLogMessage CircularDependencyDetected(this LoggerWrapper logger, string condition, string key, string dependencyCycle) => logger.LogInterpolated(
-        LogLevel.Warning, 3005,
-        $"Cannot evaluate condition ({condition}) for setting '{key}' (circular dependency detected between the following depending flags: {dependencyCycle}). Please check your feature flag definition and eliminate the circular dependency.",
-        "CONDITION", "KEY", "DEPENDENCY_CYCLE");
-
     public static FormattableLogMessage ConfigServiceCannotInitiateHttpCalls(this LoggerWrapper logger) => logger.Log(
         LogLevel.Warning, 3200,
         "Client is in offline mode, it cannot initiate HTTP calls.");