From 52817363066ab0c925c9191997d39fbca70a13a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:05:53 +0000 Subject: [PATCH 1/9] Unwrap TargetInvocationException in ReflectionMemberAccessor property accessors On Mono/tvOS (AOT environments), System.Text.Json uses ReflectionMemberAccessor which calls MethodInfo.Invoke() for property getters/setters. This wraps any exception thrown by the property in TargetInvocationException, unlike CoreCLR which uses IL-emitted delegates that propagate exceptions directly. The constructor delegates in the same class already unwrap TargetInvocationException; this commit adds the same treatment to CreatePropertyGetter and CreatePropertySetter using ExceptionDispatchInfo.Capture to preserve the original stack trace. Fixes the tvOS Mono test failure where AsyncEnumerableTests.SerializeAsyncEnumerable_PartialItemFailure tests expect InvalidOperationException but receive TargetInvocationException. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Metadata/ReflectionMemberAccessor.cs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index fd6f38ada0db7a..8fdd72e6742066 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.ExceptionServices; namespace System.Text.Json.Serialization.Metadata { @@ -167,7 +168,15 @@ public override Func CreatePropertyGetter(Property return delegate (object obj) { - return (TProperty)getMethodInfo.Invoke(obj, null)!; + try + { + return (TProperty)getMethodInfo.Invoke(obj, null)!; + } + catch (TargetInvocationException e) when (e.InnerException is not null) + { + ExceptionDispatchInfo.Capture(e.InnerException).Throw(); + throw; // unreachable + } }; } @@ -177,7 +186,15 @@ public override Func CreatePropertyGetter CreatePropertySetter(Proper return delegate (object obj, TProperty value) { - setMethodInfo.Invoke(obj, new object[] { value! }); + try + { + setMethodInfo.Invoke(obj, new object[] { value! }); + } + catch (TargetInvocationException e) when (e.InnerException is not null) + { + ExceptionDispatchInfo.Capture(e.InnerException).Throw(); + } }; } From 9579fbe27fb4c9dc211035900c3a5f2a6229c2f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Jun 2026 02:05:57 +0000 Subject: [PATCH 2/9] ci: trigger checks From c357fbb1c15470f2d0716238e6dbf8a0241fc457 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:49:01 +0000 Subject: [PATCH 3/9] Use DoNotWrapExceptions for ReflectionMemberAccessor property accessors Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Metadata/ReflectionMemberAccessor.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index 8fdd72e6742066..1f32a9a8344754 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -168,6 +168,14 @@ public override Func CreatePropertyGetter(Property return delegate (object obj) { +#if NET + return (TProperty)getMethodInfo.Invoke( + obj, + BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: null, + culture: null)!; +#else try { return (TProperty)getMethodInfo.Invoke(obj, null)!; @@ -177,6 +185,7 @@ public override Func CreatePropertyGetter(Property ExceptionDispatchInfo.Capture(e.InnerException).Throw(); throw; // unreachable } +#endif }; } @@ -186,6 +195,14 @@ public override Func CreatePropertyGetter CreatePropertyGetter CreatePropertySetter(Proper return delegate (object obj, TProperty value) { +#if NET + setMethodInfo.Invoke( + obj, + BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: new object[] { value! }, + culture: null); +#else try { setMethodInfo.Invoke(obj, new object[] { value! }); @@ -212,6 +238,7 @@ public override Action CreatePropertySetter(Proper { ExceptionDispatchInfo.Capture(e.InnerException).Throw(); } +#endif }; } From b6b94f7e49120a1163f272345ca492ced0e6e7c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:11:23 +0000 Subject: [PATCH 4/9] Apply DoNotWrapExceptions to remaining reflection Invoke calls Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Metadata/ReflectionMemberAccessor.cs | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index 1f32a9a8344754..e44e66900abc0e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -34,7 +34,16 @@ public ReflectionMemberAccessor() : null; } - return () => ctorInfo.Invoke(null); + return () => +#if NET + ctorInfo.Invoke( + BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: null, + culture: null); +#else + ctorInfo.Invoke(null); +#endif } public override Func CreateParameterizedConstructor(ConstructorInfo constructor) @@ -59,7 +68,15 @@ public override Func CreateParameterizedConstructor(ConstructorI try { +#if NET + return (T)constructor.Invoke( + BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: argsToPass, + culture: null); +#else return (T)constructor.Invoke(argsToPass); +#endif } catch (TargetInvocationException e) { @@ -109,7 +126,16 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate CreateUnionTryGetValueAccessor< chain[i] = (UnionTryGetValueAccessor)typeof(ReflectionMemberAccessor) .GetMethod(nameof(CreateUnionTryGetValueAccessorCore), BindingFlags.NonPublic | BindingFlags.Static)! .MakeGenericMethod(typeof(TUnion), entry.Key) +#if NET + .Invoke( + obj: null, + invokeAttr: BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: new object[] { entry.Value }, + culture: null)!; +#else .Invoke(null, new object[] { entry.Value })!; +#endif } return (TUnion union, out Type? caseType, out object? value) => From 5c1d4069b3e27255377145c4808aba6cd993d89c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:13:39 +0000 Subject: [PATCH 5/9] Adjust constructor invoke paths for DoNotWrapExceptions on NET only Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Metadata/ReflectionMemberAccessor.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index e44e66900abc0e..b4a169b5d1e8fc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -66,17 +66,16 @@ public override Func CreateParameterizedConstructor(ConstructorI argsToPass[i] = arguments[i]; } - try - { #if NET - return (T)constructor.Invoke( - BindingFlags.DoNotWrapExceptions, - binder: null, - parameters: argsToPass, - culture: null); + return (T)constructor.Invoke( + BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: argsToPass, + culture: null); #else + try + { return (T)constructor.Invoke(argsToPass); -#endif } catch (TargetInvocationException e) { @@ -85,6 +84,7 @@ public override Func CreateParameterizedConstructor(ConstructorI // This doesn't apply to the method below as it supports a max of 4 constructor params. throw e.InnerException ?? e; } +#endif }; } @@ -149,22 +149,22 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate { - try - { #if NET - return (T)constructor.Invoke( - BindingFlags.DoNotWrapExceptions, - binder: null, - parameters: new object?[] { value }, - culture: null); + return (T)constructor.Invoke( + BindingFlags.DoNotWrapExceptions, + binder: null, + parameters: new object?[] { value }, + culture: null); #else + try + { return (T)constructor.Invoke(new object?[] { value }); -#endif } catch (TargetInvocationException e) { throw e.InnerException ?? e; } +#endif }; } From 91768ba57ac799293268b39e8dc85cf20f9d3f4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:14:53 +0000 Subject: [PATCH 6/9] Normalize Invoke overload usage in ReflectionMemberAccessor Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Json/Serialization/Metadata/ReflectionMemberAccessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index b4a169b5d1e8fc..5da160d96667c5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -315,8 +315,8 @@ public override UnionTryGetValueAccessor CreateUnionTryGetValueAccessor< .MakeGenericMethod(typeof(TUnion), entry.Key) #if NET .Invoke( - obj: null, - invokeAttr: BindingFlags.DoNotWrapExceptions, + null, + BindingFlags.DoNotWrapExceptions, binder: null, parameters: new object[] { entry.Value }, culture: null)!; From 8a0b1d0f3b5c360a16bd917ff51bcd6b61ecb7f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:16:14 +0000 Subject: [PATCH 7/9] Clarify add-method exception semantics and remove null-forgiving cast Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Metadata/ReflectionMemberAccessor.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index 5da160d96667c5..6a4d2755c2574d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -179,6 +179,7 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate CreateUnionTryGetValueAccessor< { KeyValuePair entry = entries[i]; caseTypes[i] = entry.Key; - chain[i] = (UnionTryGetValueAccessor)typeof(ReflectionMemberAccessor) + object? accessor = typeof(ReflectionMemberAccessor) .GetMethod(nameof(CreateUnionTryGetValueAccessorCore), BindingFlags.NonPublic | BindingFlags.Static)! .MakeGenericMethod(typeof(TUnion), entry.Key) #if NET @@ -321,8 +322,15 @@ public override UnionTryGetValueAccessor CreateUnionTryGetValueAccessor< parameters: new object[] { entry.Value }, culture: null)!; #else - .Invoke(null, new object[] { entry.Value })!; + .Invoke(null, new object[] { entry.Value }); #endif + + if (accessor is null) + { + throw new InvalidOperationException(); + } + + chain[i] = (UnionTryGetValueAccessor)accessor; } return (TUnion union, out Type? caseType, out object? value) => From 102e4070bfd4d6a8fd4b651a3613ce02691ac069 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:17:31 +0000 Subject: [PATCH 8/9] Add context to union accessor creation failure Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Json/Serialization/Metadata/ReflectionMemberAccessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index 6a4d2755c2574d..d48d45e5eafb04 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -327,7 +327,7 @@ public override UnionTryGetValueAccessor CreateUnionTryGetValueAccessor< if (accessor is null) { - throw new InvalidOperationException(); + throw new InvalidOperationException($"Failed to create union accessor for type '{entry.Key}'."); } chain[i] = (UnionTryGetValueAccessor)accessor; From 6e9eede6b34d8fa05b8150937d78591c0033d9be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Jun 2026 07:18:45 +0000 Subject: [PATCH 9/9] Refine union accessor Invoke null handling Co-authored-by: kotlarmilos <11523312+kotlarmilos@users.noreply.github.com> --- .../Json/Serialization/Metadata/ReflectionMemberAccessor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs index d48d45e5eafb04..5d1c3afe69dd60 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs @@ -320,14 +320,15 @@ public override UnionTryGetValueAccessor CreateUnionTryGetValueAccessor< BindingFlags.DoNotWrapExceptions, binder: null, parameters: new object[] { entry.Value }, - culture: null)!; + culture: null); #else .Invoke(null, new object[] { entry.Value }); #endif if (accessor is null) { - throw new InvalidOperationException($"Failed to create union accessor for type '{entry.Key}'."); + throw new InvalidOperationException( + $"Failed to create union accessor for type '{entry.Key}' using method '{entry.Value}'."); } chain[i] = (UnionTryGetValueAccessor)accessor;