From e019bcf28573ce2220c77ad056f593be7440cbba Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 31 Jan 2026 23:13:48 +0000
Subject: [PATCH 1/3] Initial plan
From 9a2e10e151ffea25485445ef9b96c14f00edc107 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 31 Jan 2026 23:22:38 +0000
Subject: [PATCH 2/3] fix: Only mark properties with 'required' modifier as
required in OpenAPI schema
Properties with default initializers (like ExtraData = new()) are now correctly treated as optional unless they have the explicit 'required' modifier.
Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com>
---
.../RequiredPropertySchemaTransformer.cs | 38 ++++++++++---------
1 file changed, 21 insertions(+), 17 deletions(-)
diff --git a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs
index 13fa0bd7e7..3c4b163ae9 100644
--- a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs
+++ b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs
@@ -5,23 +5,24 @@
namespace Exceptionless.Web.Utility.OpenApi;
///
-/// Schema transformer that marks non-nullable properties as required in OpenAPI schemas.
+/// Schema transformer that marks required properties in OpenAPI schemas based on C# required modifiers and value types.
///
///
///
/// Why this exists: Microsoft.AspNetCore.OpenApi doesn't consistently detect
-/// required properties from C# nullability annotations. This causes all properties to
-/// become optional in generated schemas, even when they're non-nullable in the C# model.
+/// required properties from C# modifiers. This transformer ensures properties are marked as required
+/// when appropriate for the OpenAPI schema.
///
///
-/// This transformer inspects C# nullability context and marks properties as required when:
+/// This transformer marks properties as required when:
///
+/// - The property has the required modifier (C# 11+)
/// - The property type is a non-nullable value type (e.g., int, bool, DateTime)
-/// - The property type is a non-nullable reference type in a nullable-enabled context
///
///
///
-/// The [Required] attribute is also respected for explicit marking.
+/// Non-nullable reference types are NOT marked as required unless they have the explicit required modifier.
+/// This correctly handles properties with default initializers (e.g., public MyClass Prop { get; init; } = new();).
///
///
/// This transformer resolves property names using the effective JSON property name
@@ -82,6 +83,12 @@ private static bool IsPropertyRequired(PropertyInfo property, NullabilityInfoCon
{
var propertyType = property.PropertyType;
+ // Check if the property is marked with the 'required' modifier (C# 11+)
+ // This takes precedence over other heuristics
+ var requiredMemberAttribute = property.GetCustomAttribute();
+ if (requiredMemberAttribute is not null)
+ return true;
+
// Non-nullable value types are always required (except when wrapped in Nullable)
if (propertyType.IsValueType)
{
@@ -89,16 +96,13 @@ private static bool IsPropertyRequired(PropertyInfo property, NullabilityInfoCon
return Nullable.GetUnderlyingType(propertyType) is null;
}
- // For reference types, check nullability annotations
- try
- {
- var nullabilityInfo = nullabilityContext.Create(property);
- return nullabilityInfo.WriteState == NullabilityState.NotNull;
- }
- catch
- {
- // If we can't determine nullability, default to not required
- return false;
- }
+ // For reference types with default initializers (e.g., "= new()"), we should NOT mark them as required
+ // since they can be omitted during construction. However, we can't reliably detect initializers via reflection.
+ // Instead, we only mark reference types as required if they have the 'required' modifier (checked above).
+ // This means non-nullable reference types without 'required' are treated as optional.
+
+ // For backwards compatibility and to match expected behavior, we do NOT mark non-nullable
+ // reference types as required unless they have the explicit 'required' modifier.
+ return false;
}
}
From 7e7411f90254b40bbb5c373237e9833b50791cb0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 31 Jan 2026 23:24:22 +0000
Subject: [PATCH 3/3] chore: Address code review feedback - remove blank line
Co-authored-by: niemyjski <1020579+niemyjski@users.noreply.github.com>
---
.../Utility/OpenApi/RequiredPropertySchemaTransformer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs
index 3c4b163ae9..2bf798f302 100644
--- a/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs
+++ b/src/Exceptionless.Web/Utility/OpenApi/RequiredPropertySchemaTransformer.cs
@@ -100,7 +100,7 @@ private static bool IsPropertyRequired(PropertyInfo property, NullabilityInfoCon
// since they can be omitted during construction. However, we can't reliably detect initializers via reflection.
// Instead, we only mark reference types as required if they have the 'required' modifier (checked above).
// This means non-nullable reference types without 'required' are treated as optional.
-
+
// For backwards compatibility and to match expected behavior, we do NOT mark non-nullable
// reference types as required unless they have the explicit 'required' modifier.
return false;