Start Debugging

Solución: The 'interceptors' feature is not enabled in this namespace

CS9137 viene del generador de código fuente de Microsoft.AspNetCore.OpenApi. Agrega InterceptorsNamespaces a cada proyecto que llame a AddOpenApi, no solo al que tiene el PackageReference.

Agrega esto al proyecto que el compilador nombra en el error, no al proyecto que tiene el PackageReference:

<!-- .NET 10 / .NET 11, Microsoft.AspNetCore.OpenApi 10.0.x -->
<PropertyGroup>
  <InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
</PropertyGroup>

El generador de código fuente de comentarios XML que vive dentro de Microsoft.AspNetCore.OpenApi emite interceptores, y la propiedad de MSBuild que autoriza su espacio de nombres se distribuye en la carpeta build/ del paquete. NuGet no propaga build/ a través de un ProjectReference ni de una dependencia transitiva de paquete, pero sí propaga los analizadores. Por eso, cualquier proyecto que hereda el generador sin heredar la propiedad falla al compilar. Todo lo que sigue está verificado contra el SDK 10.0.201 y Microsoft.AspNetCore.OpenApi 10.0.10.

El error en contexto

El compilador apunta a un archivo que nunca escribiste, dentro de 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 siempre viene con un espacio de nombres adjunto, y ese espacio de nombres te dice qué generador está molesto:

Espacio de nombres en el mensajeGenerador
Microsoft.AspNetCore.OpenApi.GeneratedComentarios de documentación XML para OpenAPI
Microsoft.Extensions.Validation.GeneratedValidación de minimal APIs (.NET 10 y posteriores)
Microsoft.AspNetCore.Http.Validation.GeneratedEl mismo generador, con el nombre de la versión preliminar de .NET 10
Microsoft.AspNetCore.Http.GeneratedRequest delegate generator (minimal APIs con Native AOT)
Microsoft.Extensions.Configuration.Binder.SourceGenerationGenerador del enlazador de configuración

Solo el primero es tu problema a resolver a mano en un SDK ya publicado. Los otros tres los maneja el propio SDK de .NET, en 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>

Si todavía ves la forma Microsoft.AspNetCore.Http.Validation.Generated, estás en un SDK de versión preliminar de .NET 10. El espacio de nombres se renombró antes del lanzamiento, así que una solución copiada de un artículo de 2025 hoy es una cadena de texto sin efecto.

Por qué ocurre

Los interceptores se habilitan por espacio de nombres. Roslyn no acepta un atributo [InterceptsLocation] a menos que el espacio de nombres contenedor se le haya pasado al compilador mediante /features:InterceptorsNamespaces, y MSBuild arma ese conmutador a partir de dos propiedades, ambas reenviadas en Microsoft.CSharp.Core.targets:

<!-- SDK 10.0.201, Roslyn/Microsoft.CSharp.Core.targets -->
InterceptorsNamespaces="$(InterceptorsNamespaces)"
InterceptorsPreviewNamespaces="$(InterceptorsPreviewNamespaces)"

El generador de comentarios XML para OpenAPI emite interceptores desde .NET 10. Activa EmitCompilerGeneratedFiles y puedes leer exactamente lo que produce:

// 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());
            });
        }
    }
}

Esa es toda la característica: tu punto de llamada a AddOpenApi() se reescribe en tiempo de compilación por uno que además registra los dos transformadores que llevan tus comentarios XML. Nada en esto es opcional ni diferido, y por eso un espacio de nombres no autorizado es un error de compilación duro y no una advertencia.

El paquete habilita el espacio de nombres por ti, en un solo archivo:

<!-- 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>

Fíjate en la carpeta: build, no buildTransitive. Esa asimetría es la base de todo el error. En el propio empaquetado de ASP.NET Core, la línea es explícita:

<!-- dotnet/aspnetcore, src/OpenApi/src/Microsoft.AspNetCore.OpenApi.csproj -->
<None Include="..\build\Microsoft.AspNetCore.OpenApi.targets" Pack="true" PackagePath="build" Visible="false" />

NuGet solo propaga los recursos de buildTransitive/ a los consumidores indirectos. Los analizadores, en cambio, sí se propagan. Por lo tanto, un proyecto que recibe Microsoft.AspNetCore.OpenApi a través de un ProjectReference obtiene el generador de código fuente y nada de la infraestructura de MSBuild que hace legal su salida. La causa y la cura viven en proyectos distintos, y por eso el error parece un sinsentido la primera vez: ya agregaste la propiedad, solo que no en el proyecto del que se queja el compilador.

Un solo comando te dice de qué lado de la línea está un proyecto:

dotnet msbuild MyApi.csproj -getProperty:InterceptorsNamespaces

Un valor vacío significa que la propiedad nunca llegó. ;Microsoft.AspNetCore.OpenApi.Generated significa que sí.

Reproducción mínima

Dos proyectos. El primero tiene el paquete, el segundo solo referencia al primero:

<!-- 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>

Ambos llaman a 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 compila bien. dotnet build Modules falla con CS9137. La forma es la misma tanto si el proyecto consumidor usa Microsoft.NET.Sdk como Microsoft.NET.Sdk.Web: lo reproduje con una API creada con dotnet new web cuyo único camino hacia el paquete era un ProjectReference a una biblioteca de configuración compartida. Ser un proyecto web no te salva de nada aquí.

Las bases de código reales caen en esto de tres formas reconocibles: una biblioteca estilo ServiceDefaults de Aspire que centraliza AddOpenApi, un monolito modular donde cada módulo registra su propio documento, y un host de plugins o CMS (los módulos de Umbraco y de OrchardCore aparecen en los reportes de incidencias) donde el paquete vive en un paquete base un nivel más arriba.

La solución, en orden de preferencia

1. Agrega la propiedad al proyecto que falla

Copia el espacio de nombres del mensaje de error y pega la propiedad en el .csproj que el error nombra:

<!-- .NET 10 / .NET 11, Microsoft.AspNetCore.OpenApi 10.0.x -->
<PropertyGroup>
  <InterceptorsNamespaces>$(InterceptorsNamespaces);Microsoft.AspNetCore.OpenApi.Generated</InterceptorsNamespaces>
</PropertyGroup>

Conserva el $(InterceptorsNamespaces); inicial. Otros generadores agregan valores a la misma propiedad, y una asignación pelada tira sus entradas a la basura. Así el generador sigue funcionando, de modo que los comentarios XML siguen llegando al documento.

Si varios proyectos de la solución registran documentos, ponlo una sola vez en Directory.Build.props en lugar de repetirlo. Los targets del paquete se importan después del cuerpo del proyecto y agregan a lo que encuentren, así que nunca pelean entre sí.

2. Dale al proyecto su propio PackageReference

Si el proyecto realmente llama a AddOpenApi, tiene derecho legítimo al paquete:

dotnet add Modules package Microsoft.AspNetCore.OpenApi

Eso arrastra build/Microsoft.AspNetCore.OpenApi.targets, que fija la propiedad por ti. Verifiqué que esto elimina el error sin ningún otro cambio. Es más honesto que el arreglo transitivo y sobrevive a que alguien reorganice después las referencias de proyecto.

3. Apaga el generador en ese proyecto

Si al proyecto no le interesan los comentarios XML, quita el analizador en lugar de autorizar su salida. La documentación oficial muestra esto con la propiedad de ruta del paquete; la forma basada en items es menos frágil:

<!-- .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>

La compilación se pone en verde, y el costo es que el texto de <summary> y <param> deja de aparecer en la salida OpenAPI de ese proyecto. El mismo efecto, aplicado desde el otro extremo, se logra con PrivateAssets="analyzers" en el PackageReference de arriba, que impide que el generador llegue a cualquier consumidor de esa biblioteca:

<!-- Defaults.csproj -- .NET 10, keeps the generator out of downstream projects -->
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" PrivateAssets="analyzers" />

Ambas son soluciones verificadas. Recurre a ellas cuando un servidor de compilación está en rojo y necesitas recuperar el pipeline antes que la documentación.

Detalles que mantienen la compilación en rojo

InterceptorsPreviewNamespaces todavía funciona, y no es la forma moderna. El compilador de C# 14 del SDK 10.0.201 respeta la propiedad antigua, sin advertencias, y el propio SDK la sigue usando para sus tres espacios de nombres. Usa InterceptorsNamespaces en código nuevo, pero no “arregles” una compilación heredada renombrando una propiedad que ya funciona.

Apagar la documentación XML no apaga el generador. Poner <GenerateDocumentationFile>false</GenerateDocumentationFile> parece que debería esquivar un generador de comentarios XML. No lo hace. El generador sigue interceptando AddOpenApi, y el mismo CS9137 vuelve en la línea 597 en lugar de la 598. Si quieres que desaparezca, quita el analizador.

La interceptación es por punto de llamada y por compilación. Esta le cuesta horas a mucha gente después de tener la compilación en verde. Los transformadores quedan incrustados en la compilación donde aparece físicamente la llamada AddOpenApi(), y llevan únicamente los comentarios XML de esa compilación. Mueve la llamada a una biblioteca de configuración compartida y los resúmenes de tu proyecto de API desaparecen del documento en silencio. Medido sobre la reproducción de arriba, con AddOpenApi() en línea dentro del proyecto de API:

{
  "summary": "Gets a single widget by id.",
  "parameters": [
    { "name": "id", "description": "The widget identifier." }
  ]
}

Con el mismo endpoint documentado de la misma forma, pero llamando a AddOpenApi() desde la biblioteca referenciada, tanto summary como la description del parámetro están ausentes. Mantén la llamada a AddOpenApi() en el proyecto cuyos comentarios quieres, o alimenta los archivos XML de los otros ensamblados mediante AdditionalFiles.

Un nombre de documento que no sea literal no se intercepta. AddOpenApi(documentName) donde documentName es una variable no produce ningún interceptor, así que no obtienes error ni comentarios XML. Solo se reconocen las cadenas literales.

EmitCompilerGeneratedFiles puede crear un segundo error, peor. Inspeccionar el archivo generado es el instinto correcto, pero si además apuntas CompilerGeneratedFilesOutputPath a una carpeta dentro del directorio del proyecto, el archivo emitido se convierte en una entrada real de la compilación en el siguiente build:

Generated\...\OpenApiXmlCommentSupport.generated.cs(67,42):
error CS0433: The type 'XmlComment' exists in both 'Api' and 'Api'

Deja la ruta de salida dentro de obj, o agrega un <Compile Remove> que la excluya.

Los interceptores no son el problema de LangVersion que aparentan. Son legales desde C# 12, así que un <LangVersion>12.0</LangVersion> explícito compila el archivo generado sin problema una vez que el espacio de nombres está autorizado. No te pongas a perseguir versiones de lenguaje.

Revisa todo el grafo, no un solo proyecto. Una vez que arreglas el proyecto nombrado en el error, el siguiente proyecto hacia arriba en la cadena suele fallar igual en la siguiente compilación. dotnet msbuild <project> -getProperty:InterceptorsNamespaces sobre toda la solución los encuentra a todos de una pasada.

Relacionado

El generador detrás de este error es la misma maquinaria que se describe en qué es un generador de código fuente y cuándo lo necesitas, y el mecanismo de interceptores en sí está cubierto en interceptores de C# 12. Si llegaste aquí a mitad de una actualización, el otro error de compilación de esta familia de paquetes es el tipo OpenApiReference que ya no existe, y el movimiento más amplio está en migrar de Swashbuckle al generador de OpenAPI integrado. Cuando el documento vuelva a compilar, personalizarlo con transformadores de operación y de esquema es el paso siguiente, y servirlo con Scalar le pone una interfaz encima.

Fuentes

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Volver