diff --git a/src/GenerativeAI/Core/ResponseHelper.cs b/src/GenerativeAI/Core/ResponseHelper.cs
index e0d10ef..06d843f 100644
--- a/src/GenerativeAI/Core/ResponseHelper.cs
+++ b/src/GenerativeAI/Core/ResponseHelper.cs
@@ -73,6 +73,13 @@ internal static string FormatErrorMessage(FinishReason response)
FinishReason.SPII => "The generation stopped because the content might include Sensitive Personally Identifiable Information (SPII).",
FinishReason.MALFORMED_FUNCTION_CALL => "The generation stopped because a malformed or invalid function call was generated by the model.",
FinishReason.IMAGE_SAFETY => "The generation stopped because the generated images were flagged for containing safety violations.",
+ FinishReason.MODEL_ARMOR => "The generation stopped due to model armor protection mechanisms.",
+ FinishReason.IMAGE_PROHIBITED_CONTENT => "Image generation stopped because the generated images contain prohibited content.",
+ FinishReason.IMAGE_RECITATION => "Image generation stopped because the generated images were flagged for recitation.",
+ FinishReason.IMAGE_OTHER => "Image generation stopped due to other miscellaneous issues.",
+ FinishReason.UNEXPECTED_TOOL_CALL => "The generation stopped because the model generated a tool call but no tools were enabled in the request.",
+ FinishReason.NO_IMAGE => "The generation stopped because the model was expected to generate an image, but none was generated.",
+ FinishReason.TOO_MANY_TOOL_CALLS => "The generation stopped because the model called too many tools consecutively.",
_ => "The generation stopped for an unexpected or unhandled reason."
};
}
diff --git a/src/GenerativeAI/Types/ContentGeneration/Outputs/FinishReason.cs b/src/GenerativeAI/Types/ContentGeneration/Outputs/FinishReason.cs
index db084d3..05e04c7 100644
--- a/src/GenerativeAI/Types/ContentGeneration/Outputs/FinishReason.cs
+++ b/src/GenerativeAI/Types/ContentGeneration/Outputs/FinishReason.cs
@@ -1,12 +1,18 @@
using System.Text.Json.Serialization;
+using GenerativeAI.Types.Converters;
namespace GenerativeAI.Types;
///
/// Defines the reason why the model stopped generating tokens.
///
+///
+/// This enum uses a lenient JSON converter that gracefully handles unknown values
+/// by falling back to instead of throwing an exception.
+/// This prevents crashes when Google adds new FinishReason values to their API.
+///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(LenientFinishReasonConverter))]
public enum FinishReason
{
///
@@ -69,4 +75,46 @@ public enum FinishReason
/// Token generation stopped because generated images contain safety violations.
///
IMAGE_SAFETY = 11,
+
+ ///
+ /// Token generation stopped due to model armor protection mechanisms.
+ /// Documented in Vertex AI (google.cloud.aiplatform.v1).
+ ///
+ MODEL_ARMOR = 12,
+
+ ///
+ /// Image generation stopped because generated images have prohibited content.
+ /// Documented in Vertex AI (google.cloud.aiplatform.v1).
+ ///
+ IMAGE_PROHIBITED_CONTENT = 13,
+
+ ///
+ /// Image generation stopped because generated images were flagged for recitation.
+ /// Documented in Vertex AI (google.cloud.aiplatform.v1).
+ ///
+ IMAGE_RECITATION = 14,
+
+ ///
+ /// Image generation stopped because of other miscellaneous issues.
+ /// Documented in Vertex AI (google.cloud.aiplatform.v1).
+ ///
+ IMAGE_OTHER = 15,
+
+ ///
+ /// Model generated a tool call but no tools were enabled in the request.
+ /// Documented in Gemini API and Vertex AI.
+ ///
+ UNEXPECTED_TOOL_CALL = 16,
+
+ ///
+ /// The model was expected to generate an image, but none was generated.
+ /// Documented in Vertex AI (google.cloud.aiplatform.v1).
+ ///
+ NO_IMAGE = 17,
+
+ ///
+ /// Model called too many tools consecutively, thus the system exited execution.
+ /// Documented in generativelanguagepb (SDK/proto contract).
+ ///
+ TOO_MANY_TOOL_CALLS = 18,
}
\ No newline at end of file
diff --git a/src/GenerativeAI/Types/Converters/LenientFinishReasonConverter.cs b/src/GenerativeAI/Types/Converters/LenientFinishReasonConverter.cs
new file mode 100644
index 0000000..aa9cd24
--- /dev/null
+++ b/src/GenerativeAI/Types/Converters/LenientFinishReasonConverter.cs
@@ -0,0 +1,45 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace GenerativeAI.Types.Converters;
+
+///
+/// Lenient JSON converter for FinishReason that handles unknown enum values gracefully.
+/// When an unknown string value is encountered, it falls back to FinishReason.OTHER
+/// instead of throwing an exception. This prevents crashes when Google adds new
+/// FinishReason values to their API.
+///
+public class LenientFinishReasonConverter : JsonConverter
+{
+ ///
+ /// Reads and converts the JSON to FinishReason.
+ ///
+ public override FinishReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ throw new JsonException($"Expected string value for FinishReason, got {reader.TokenType}");
+ }
+
+ var value = reader.GetString();
+ if (string.IsNullOrEmpty(value))
+ {
+ return FinishReason.FINISH_REASON_UNSPECIFIED;
+ }
+
+ if (Enum.TryParse(value, ignoreCase: true, out var result))
+ {
+ return result;
+ }
+
+ return FinishReason.OTHER;
+ }
+
+ ///
+ /// Writes the FinishReason value as JSON.
+ ///
+ public override void Write(Utf8JsonWriter writer, FinishReason value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.ToString());
+ }
+}
diff --git a/tests/GenerativeAI.Tests/Converters/LenientFinishReasonConverter_Tests.cs b/tests/GenerativeAI.Tests/Converters/LenientFinishReasonConverter_Tests.cs
new file mode 100644
index 0000000..8c0f222
--- /dev/null
+++ b/tests/GenerativeAI.Tests/Converters/LenientFinishReasonConverter_Tests.cs
@@ -0,0 +1,32 @@
+using System.Text.Json;
+using GenerativeAI.Types;
+using Shouldly;
+
+namespace GenerativeAI.Tests.Converters;
+
+public class LenientFinishReasonConverter_Tests
+{
+ [Fact]
+ public void Read_KnownValues_DeserializeCorrectly()
+ {
+ // Old value
+ JsonSerializer.Deserialize("\"STOP\"").ShouldBe(FinishReason.STOP);
+
+ // New value with lowercase (tests case insensitivity)
+ JsonSerializer.Deserialize("\"unexpected_tool_call\"").ShouldBe(FinishReason.UNEXPECTED_TOOL_CALL);
+ }
+
+ [Fact]
+ public void Read_UnknownValue_FallsBackToOther()
+ {
+ var result = JsonSerializer.Deserialize("\"UNKNOWN_FUTURE_VALUE\"");
+ result.ShouldBe(FinishReason.OTHER);
+ }
+
+ [Fact]
+ public void Write_EnumValue_SerializesCorrectly()
+ {
+ var json = JsonSerializer.Serialize(FinishReason.UNEXPECTED_TOOL_CALL);
+ json.ShouldBe("\"UNEXPECTED_TOOL_CALL\"");
+ }
+}