Исправление: The 'interceptors' feature is not enabled in this namespace
CS9137 приходит от генератора исходного кода в Microsoft.AspNetCore.OpenApi. Добавьте InterceptorsNamespaces в каждый проект, который вызывает AddOpenApi, а не только в тот, где лежит PackageReference.
Добавьте это в тот проект, который компилятор называет в ошибке, а не в проект с PackageReference:
<!-- .NET 10 / .NET 11, Microsoft.AspNetCore.OpenApi 10.0.x -->
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
</PropertyGroup>
Генератор исходного кода для XML-комментариев внутри Microsoft.AspNetCore.OpenApi выпускает перехватчики, а свойство MSBuild, которое разрешает их пространство имён, поставляется в папке build/ пакета. NuGet не передаёт build/ ни через ProjectReference, ни через транзитивную зависимость пакета, а вот анализаторы передаёт. Поэтому любой проект, который унаследовал генератор, но не свойство, перестаёт компилироваться. Всё, что описано ниже, проверено на SDK 10.0.201 и Microsoft.AspNetCore.OpenApi 10.0.10.
Ошибка в контексте
Компилятор указывает на файл, который вы никогда не писали, внутри obj:
obj\Debug\net10.0\Microsoft.AspNetCore.OpenApi.SourceGenerators\Microsoft.AspNetCore.OpenApi.SourceGenerators.XmlCommentGenerator\OpenApiXmlCommentSupport.generated.cs(598,10):
error CS9137: The 'interceptors' feature is not enabled in this namespace. Add '<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>' to your project.
CS9137 всегда приходит с пространством имён, и по нему понятно, какой генератор недоволен:
| Пространство имён в сообщении | Генератор |
|---|---|
Microsoft.AspNetCore.OpenApi.Generated | XML-комментарии документации для OpenAPI |
Microsoft.Extensions.Validation.Generated | Валидация minimal API (.NET 10 и новее) |
Microsoft.AspNetCore.Http.Validation.Generated | Тот же генератор, имя из предварительной версии .NET 10 |
Microsoft.AspNetCore.Http.Generated | Request delegate generator (minimal API с Native AOT) |
Microsoft.Extensions.Configuration.Binder.SourceGeneration | Генератор привязки конфигурации |
На выпущенном SDK руками решать нужно только первый случай. Остальные три .NET SDK закрывает сам, в Microsoft.NET.Sdk.FrameworkReferenceResolution.targets:
<!-- SDK 10.0.201, Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets -->
<InterceptorsPreviewNamespaces Condition="'$(EnableRequestDelegateGenerator)' == 'true'">$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated</InterceptorsPreviewNamespaces>
<InterceptorsPreviewNamespaces Condition="'$(EnableConfigurationBindingGenerator)' == 'true'">$(InterceptorsPreviewNamespaces);Microsoft.Extensions.Configuration.Binder.SourceGeneration</InterceptorsPreviewNamespaces>
<InterceptorsPreviewNamespaces Condition="'$(_TargetFrameworkVersionWithoutV)' != '' and $([MSBuild]::VersionGreaterThanOrEquals('$(_TargetFrameworkVersionWithoutV)', '10.0'))">$(InterceptorsPreviewNamespaces);Microsoft.Extensions.Validation.Generated</InterceptorsPreviewNamespaces>
Если вы всё ещё видите написание Microsoft.AspNetCore.Http.Validation.Generated, значит, вы работаете на предварительной версии SDK для .NET 10. Пространство имён переименовали до релиза, поэтому исправление, скопированное из статьи 2025 года, сегодня представляет собой строку без эффекта.
Почему это происходит
Перехватчики включаются по пространствам имён. Roslyn не примет атрибут [InterceptsLocation], пока содержащее его пространство имён не передано компилятору через /features:InterceptorsNamespaces, а MSBuild собирает этот ключ из двух свойств, оба из которых пробрасываются в Microsoft.CSharp.Core.targets:
<!-- SDK 10.0.201, Roslyn/Microsoft.CSharp.Core.targets -->
InterceptorsNamespaces="$(InterceptorsNamespaces)"
InterceptorsPreviewNamespaces="$(InterceptorsPreviewNamespaces)"
Генератор XML-комментариев для OpenAPI выпускает перехватчики начиная с .NET 10. Включите EmitCompilerGeneratedFiles, и можно прочитать, что именно он производит:
// Generated by Microsoft.AspNetCore.OpenApi.SourceGenerators 10.0.10
namespace Microsoft.AspNetCore.OpenApi.Generated
{
file static class GeneratedServiceCollectionExtensions
{
[global::System.Runtime.CompilerServices.InterceptsLocationAttribute(1, "iPD7FWRAJiLeb9V88cfKbz0BAABDbGFzczEuY3M=")]
public static IServiceCollection AddOpenApi(this IServiceCollection services, string documentName)
{
return services.AddOpenApi(documentName, options =>
{
options.AddSchemaTransformer(new XmlCommentSchemaTransformer());
options.AddOperationTransformer(new XmlCommentOperationTransformer());
});
}
}
}
В этом вся возможность: место вызова AddOpenApi() во время компиляции переписывается на такое, которое дополнительно регистрирует два преобразователя с вашими XML-комментариями. Ничего необязательного или отложенного здесь нет, поэтому неразрешённое пространство имён и приводит к жёсткой ошибке сборки, а не к предупреждению.
Пакет разрешает пространство имён за вас, в одном файле:
<!-- microsoft.aspnetcore.openapi/10.0.10/build/Microsoft.AspNetCore.OpenApi.targets -->
<Project>
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
</PropertyGroup>
<Target Name="GenerateAdditionalXmlFilesForOpenApi" AfterTargets="ResolveReferences">
...
</Target>
</Project>
Обратите внимание на папку: build, а не buildTransitive. На этой асимметрии и держится вся ошибка. В упаковке самого ASP.NET Core строка задана явно:
<!-- dotnet/aspnetcore, src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj -->
<None Include="..\build\Microsoft.AspNetCore.OpenApi.targets" Pack="true" PackagePath="build" Visible="false" />
NuGet передаёт косвенным потребителям только ресурсы из buildTransitive/. Анализаторы, наоборот, передаются. Поэтому проект, который получает Microsoft.AspNetCore.OpenApi через ProjectReference, забирает генератор исходного кода и ничего из инфраструктуры MSBuild, которая делает его вывод допустимым. Причина и лекарство лежат в разных проектах, и потому ошибка в первый раз выглядит бессмыслицей: свойство вы уже добавили, просто не в тот проект, на который жалуется компилятор.
Одна команда показывает, по какую сторону границы находится проект:
dotnet msbuild MyApi.csproj -getProperty:InterceptorsNamespaces
Пустое значение означает, что свойство так и не пришло. ;Microsoft.AspNetCore.OpenApi.Generated означает, что пришло.
Минимальное воспроизведение
Два проекта. У первого есть пакет, второй только ссылается на первый:
<!-- Defaults.csproj -- .NET 10, Microsoft.AspNetCore.OpenApi 10.0.10 -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
</ItemGroup>
</Project>
<!-- Modules.csproj -- .NET 10, no PackageReference of its own -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Defaults\Defaults.csproj" />
</ItemGroup>
</Project>
Оба вызывают AddOpenApi:
// Modules/Class1.cs -- .NET 10, C# 14
using Microsoft.Extensions.DependencyInjection;
namespace Modules;
/// <summary>Module registrations.</summary>
public static class ModuleExtensions
{
/// <summary>Adds the module's OpenAPI document.</summary>
public static IServiceCollection AddModule(this IServiceCollection services)
=> services.AddOpenApi("module");
}
dotnet build Defaults проходит. dotnet build Modules падает с CS9137. Картина одинакова и для Microsoft.NET.Sdk, и для Microsoft.NET.Sdk.Web в потребляющем проекте: я воспроизвёл это на API, созданном через dotnet new web, у которого единственный путь к пакету шёл через ProjectReference на общую библиотеку настроек. Быть веб-проектом здесь не помогает.
Реальные кодовые базы попадают сюда тремя узнаваемыми способами: библиотека в стиле ServiceDefaults из Aspire, которая централизует AddOpenApi, модульный монолит, где каждый модуль регистрирует свой документ, и хост плагинов или CMS (в отчётах об ошибках встречаются модули Umbraco и OrchardCore), где пакет лежит уровнем выше в базовом пакете.
Исправление, в порядке предпочтения
1. Добавьте свойство в падающий проект
Скопируйте пространство имён из текста ошибки и вставьте свойство в тот .csproj, который назван в ошибке:
<!-- .NET 10 / .NET 11, Microsoft.AspNetCore.OpenApi 10.0.x -->
<PropertyGroup>
<InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
</PropertyGroup>
Сохраните ведущее $(InterceptorsNamespaces);. Другие генераторы дописывают значения в это же свойство, а голое присваивание выбрасывает их записи. При таком варианте генератор продолжает работать, и XML-комментарии по-прежнему попадают в документ.
Если документы регистрируют несколько проектов решения, вынесите строку один раз в Directory.Build.props, а не повторяйте её. Targets пакета импортируются после тела проекта и дописывают к тому, что нашли, так что конфликта не возникает.
2. Дайте проекту собственный PackageReference
Если проект действительно вызывает AddOpenApi, у него есть законное право на пакет:
dotnet add Modules package Microsoft.AspNetCore.OpenApi
Вместе с пакетом приходит build/Microsoft.AspNetCore.OpenApi.targets, который задаёт свойство за вас. Я проверил, что это убирает ошибку без каких-либо других изменений. Такой вариант честнее транзитивной схемы и переживает последующую перестройку ссылок между проектами.
3. Отключите генератор в этом проекте
Если проекту XML-комментарии не нужны, удалите анализатор вместо того, чтобы разрешать его вывод. Официальная документация показывает это через свойство пути к пакету; форма на основе элементов менее хрупкая:
<!-- .NET 10, disables the OpenAPI XML comment generator for this project only -->
<Target Name="DisableOpenApiXmlGenerator" BeforeTargets="CoreCompile">
<ItemGroup>
<Analyzer Remove="@(Analyzer)"
Condition="'%(Filename)' == 'Microsoft.AspNetCore.OpenApi.SourceGenerators'" />
</ItemGroup>
</Target>
Сборка становится зелёной, а цена в том, что текст из <summary> и <param> перестаёт появляться в выводе OpenAPI этого проекта. Тот же эффект с другого конца даёт PrivateAssets="analyzers" на вышестоящем PackageReference: генератор перестаёт доходить до любых потребителей этой библиотеки:
<!-- Defaults.csproj -- .NET 10, keeps the generator out of downstream projects -->
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" PrivateAssets="analyzers" />
Оба варианта проверены. Берите их, когда сервер сборки горит красным и конвейер нужен раньше, чем документация.
Мелочи, из-за которых сборка остаётся красной
InterceptorsPreviewNamespaces всё ещё работает и не является современным написанием. Компилятор C# 14 в SDK 10.0.201 уважает старое свойство и не выдаёт предупреждений, а сам SDK продолжает использовать его для своих трёх пространств имён. В новом коде пишите InterceptorsNamespaces, но не «чините» унаследованную сборку переименованием уже работающего свойства.
Отключение XML-документации не отключает генератор. Кажется, что <GenerateDocumentationFile>false</GenerateDocumentationFile> должно обойти генератор XML-комментариев. Не обходит. Генератор по-прежнему перехватывает AddOpenApi, и тот же CS9137 возвращается в строке 597 вместо 598. Если он вам не нужен, удаляйте анализатор.
Перехват работает по месту вызова и по компиляции. Именно это стоит людям часов уже после того, как сборка позеленела. Преобразователи запекаются в ту компиляцию, где физически находится вызов AddOpenApi(), и несут только XML-комментарии этой компиляции. Перенесите вызов в общую библиотеку настроек, и краткие описания вашего проекта API молча исчезнут из документа. Замерено на воспроизведении выше, с AddOpenApi() прямо в проекте API:
{
"summary": "Gets a single widget by id.",
"parameters": [
{ "name": "id", "description": "The widget identifier." }
]
}
При том же endpoint, документированном точно так же, но с вызовом AddOpenApi() из библиотеки по ссылке, отсутствуют и summary, и description параметра. Держите вызов AddOpenApi() в том проекте, чьи комментарии вам нужны, либо подавайте XML-файлы других сборок через AdditionalFiles.
Нелитеральное имя документа не перехватывается. AddOpenApi(documentName), где documentName является переменной, не порождает перехватчика вовсе, поэтому вы не получите ни ошибки, ни XML-комментариев. Распознаются только литеральные строки.
EmitCompilerGeneratedFiles может создать вторую, худшую ошибку. Заглянуть в сгенерированный файл - правильный инстинкт, но если вы вдобавок направите CompilerGeneratedFilesOutputPath в папку внутри каталога проекта, то на следующей сборке выведенный файл станет настоящим входом компиляции:
Generated\...\OpenApiXmlCommentSupport.generated.cs(67,42):
error CS0433: The type 'XmlComment' exists in both 'Api' and 'Api'
Оставьте путь вывода внутри obj или добавьте соответствующий <Compile Remove>.
Перехватчики не являются той проблемой LangVersion, на которую похожи. Они допустимы начиная с C# 12, поэтому явный <LangVersion>12.0</LangVersion> спокойно компилирует сгенерированный файл, как только пространство имён разрешено. Не гоняйтесь за версиями языка.
Проверяйте весь граф, а не один проект. Как только вы почините проект, названный в ошибке, следующий проект выше по цепочке на очередной сборке часто падает точно так же. dotnet msbuild <project> -getProperty:InterceptorsNamespaces по всему решению находит их все за один проход.
Похожие материалы
Генератор за этой ошибкой - та же машинерия, что описана в статье что такое генератор исходного кода и когда он нужен, а сам механизм перехватчиков разобран в перехватчиках C# 12. Если вы попали сюда посреди обновления, вторая поломка сборки в этом семействе пакетов - исчезнувший тип OpenApiReference, а более крупный переезд описан в миграции со Swashbuckle на встроенный генератор OpenAPI. Когда документ снова соберётся, следующий шаг - настройка через преобразователи операций и схем, а публикация через Scalar добавляет к нему интерфейс.
Источники
- Поддержка XML-комментариев документации в OpenAPI для ASP.NET Core, где описаны перехватываемые перегрузки
AddOpenApi, ограничение на литеральные строки и рецепт удаления анализатора. - Issue 61177 в dotnet/aspnetcore, отслеживающая задача для проектов, которым приходится добавлять
InterceptorsNamespacesвручную. - Issue 77877 в dotnet/roslyn, отчёт о CS9137 на SDK предварительной версии 3 для .NET 10, закрытый как дубликат задачи ASP.NET Core.
- Issue 63623 в dotnet/aspnetcore, сообщение о том, что ошибка сохраняется после добавления свойства.
- Microsoft.AspNetCore.OpenApi.csproj, где файл targets упаковывается в
build, а не вbuildTransitive.
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.