diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index adf5fd769..bd1ba1cb3 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "2.4.1"
+ ".": "2.4.2"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7414944db..b7d9b73f6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## [2.4.2](https://github.com/microsoft/OpenAPI.NET/compare/v2.4.1...v2.4.2) (2025-12-22)
+
+
+### Bug Fixes
+
+* wrap extension parser calls in try-catch to ensure correct error pointers ([63cf4a3](https://github.com/microsoft/OpenAPI.NET/commit/63cf4a3029fe2d285a6fe2724e30ffcf3bdd2f9f))
+* wrap extension parser calls in try-catch to ensure correct error pointers ([458cabe](https://github.com/microsoft/OpenAPI.NET/commit/458cabe6cd0fbdb192dbd17f2d6ab3b8162d1166))
+
## [2.4.1](https://github.com/microsoft/OpenAPI.NET/compare/v2.4.0...v2.4.1) (2025-12-18)
diff --git a/Directory.Build.props b/Directory.Build.props
index 2d0fc7255..a64e378aa 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -12,7 +12,7 @@
https://github.com/Microsoft/OpenAPI.NET
© Microsoft Corporation. All rights reserved.
OpenAPI .NET
- 2.4.1
+ 2.4.2
diff --git a/src/Microsoft.OpenApi/Reader/V2/OpenApiV2Deserializer.cs b/src/Microsoft.OpenApi/Reader/V2/OpenApiV2Deserializer.cs
index c640b310c..80d079b5e 100644
--- a/src/Microsoft.OpenApi/Reader/V2/OpenApiV2Deserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V2/OpenApiV2Deserializer.cs
@@ -79,12 +79,18 @@ private static IOpenApiExtension LoadExtension(string name, ParseNode node)
{
if (node.Context.ExtensionParsers is not null && node.Context.ExtensionParsers.TryGetValue(name, out var parser))
{
- return parser(node.CreateAny(), OpenApiSpecVersion.OpenApi2_0);
- }
- else
- {
- return new JsonNodeExtension(node.CreateAny());
+ try
+ {
+ return parser(node.CreateAny(), OpenApiSpecVersion.OpenApi2_0);
+ }
+ catch (OpenApiException ex)
+ {
+ ex.Pointer = node.Context.GetLocation();
+ node.Context.Diagnostic.Errors.Add(new(ex));
+ }
}
+
+ return new JsonNodeExtension(node.CreateAny());
}
private static string? LoadString(ParseNode node)
diff --git a/src/Microsoft.OpenApi/Reader/V3/OpenApiV3Deserializer.cs b/src/Microsoft.OpenApi/Reader/V3/OpenApiV3Deserializer.cs
index 0b74cedc5..1a03268d6 100644
--- a/src/Microsoft.OpenApi/Reader/V3/OpenApiV3Deserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V3/OpenApiV3Deserializer.cs
@@ -130,15 +130,24 @@ public static JsonNodeExtension LoadAny(ParseNode node, OpenApiDocument hostDocu
private static IOpenApiExtension LoadExtension(string name, ParseNode node)
{
- if (node.Context.ExtensionParsers is not null && node.Context.ExtensionParsers.TryGetValue(name, out var parser) && parser(
- node.CreateAny(), OpenApiSpecVersion.OpenApi3_0) is { } result)
+ if (node.Context.ExtensionParsers is not null && node.Context.ExtensionParsers.TryGetValue(name, out var parser))
{
- return result;
- }
- else
- {
- return new JsonNodeExtension(node.CreateAny());
+ try
+ {
+ var result = parser(node.CreateAny(), OpenApiSpecVersion.OpenApi3_0);
+ if (result is { })
+ {
+ return result;
+ }
+ }
+ catch (OpenApiException ex)
+ {
+ ex.Pointer = node.Context.GetLocation();
+ node.Context.Diagnostic.Errors.Add(new(ex));
+ }
}
+
+ return new JsonNodeExtension(node.CreateAny());
}
private static string? LoadString(ParseNode node)
diff --git a/src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs b/src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs
index 08f2ec048..3608ad5f7 100644
--- a/src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs
+++ b/src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs
@@ -131,9 +131,20 @@ public static JsonNode LoadAny(ParseNode node, OpenApiDocument hostDocument)
private static IOpenApiExtension LoadExtension(string name, ParseNode node)
{
- return node.Context.ExtensionParsers is not null && node.Context.ExtensionParsers.TryGetValue(name, out var parser)
- ? parser(node.CreateAny(), OpenApiSpecVersion.OpenApi3_1)
- : new JsonNodeExtension(node.CreateAny());
+ if (node.Context.ExtensionParsers is not null && node.Context.ExtensionParsers.TryGetValue(name, out var parser))
+ {
+ try
+ {
+ return parser(node.CreateAny(), OpenApiSpecVersion.OpenApi3_1);
+ }
+ catch (OpenApiException ex)
+ {
+ ex.Pointer = node.Context.GetLocation();
+ node.Context.Diagnostic.Errors.Add(new(ex));
+ }
+ }
+
+ return new JsonNodeExtension(node.CreateAny());
}
private static string? LoadString(ParseNode node)
diff --git a/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs b/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs
index 57f55e95e..96de89cf9 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/TestCustomExtension.cs
@@ -44,6 +44,80 @@ public void ParseCustomExtension()
Assert.Equal("hey", fooExtension.Bar);
Assert.Equal("hi!", fooExtension.Baz);
}
+
+ [Fact]
+ public void ExtensionParserThrowingOpenApiException_V2_ShouldHaveCorrectPointer()
+ {
+ var json = """
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "Demo",
+ "version": "1"
+ },
+ "paths": {},
+ "definitions": {
+ "demo": {
+ "x-tag": null
+ }
+ }
+}
+""";
+ var settings = new OpenApiReaderSettings
+ {
+ ExtensionParsers =
+ {
+ { "x-tag", (any, version) => throw new OpenApiException("Testing") }
+ }
+ };
+
+ var result = OpenApiDocument.Parse(json, "json", settings);
+
+ Assert.NotNull(result.Diagnostic);
+ Assert.NotEmpty(result.Diagnostic.Errors);
+ var error = result.Diagnostic.Errors[0];
+ Assert.Equal("Testing", error.Message);
+ Assert.Equal("#/definitions/demo/x-tag", error.Pointer);
+ }
+
+ [Theory]
+ [InlineData("3.0.4")]
+ [InlineData("3.1.1")]
+ public void ExtensionParserThrowingOpenApiException_V3_ShouldHaveCorrectPointer(string version)
+ {
+ var json = $$"""
+{
+ "openapi": "{{version}}",
+ "info": {
+ "title": "Demo",
+ "version": "1"
+ },
+ "paths": {},
+ "components": {
+ "schemas": {
+ "demo": {
+ "x-tag": null
+ }
+ }
+ }
+}
+""";
+ var settings = new OpenApiReaderSettings
+ {
+ ExtensionParsers =
+ {
+ { "x-tag", (any, version) => throw new OpenApiException("Testing") }
+ }
+ };
+
+ var result = OpenApiDocument.Parse(json, "json", settings);
+
+ Assert.NotNull(result.Diagnostic);
+ Assert.NotEmpty(result.Diagnostic.Errors);
+ var error = result.Diagnostic.Errors[0];
+ Assert.Equal("Testing", error.Message);
+ Assert.Equal("#/components/schemas/demo/x-tag", error.Pointer);
+ }
}
internal class FooExtension : IOpenApiExtension, IOpenApiElement