PAYATHON 2026

Microsoft.WebTools.Aspire.targets 的“<”解析错误

排障赵师傅

结论

Microsoft.WebTools.Aspire.targets 通常不存在 XML 语法错误,Azure.Identity 也没有在运行时调用 Aspire。报错发生在 Visual Studio Web Tools 的设计时构建阶段:GenerateCombinedComponentSchema 或 GenerateCombinedAppSettingsSchema 任务合并 JSON Schema 时,读到了以 < 开头的 XML 内容,于是 System.Text.Json 报错:

'<' is an invalid start of a value.

错误位置指向 .targets 中的 <GenerateCombinedAppSettingsSchema>,说明异常是在此处调用 MSBuild task 时抛出的。这不代表该段 XML 解析失败,也不需要直接修改这个文件。

如果问题确实从 Azure.Identity 1.20.0 开始出现,可能的触发过程是:NuGet restore 改变了设计时构建的输入,或者改变了传入的 @(JsonSchemaSegment),Visual Studio 安装的 Web Tools 任务随后在处理这些输入时失败。现有错误不足以判断问题来自 Azure.Identity 包、Visual Studio Web Tools 的兼容性,还是本机 NuGet 缓存损坏。要确定原因,必须查看实际传给 MSBuild 的输入。

IEnumerable 失去语义着色和悬停说明,很可能也是设计时构建失败引起的。Roslyn 无法完整取得引用、编译选项或生成文件,编辑器的语义服务便无法正常工作。项目仍可运行,是因为普通构建、已有输出和设计时构建执行的目标并不完全相同。

为什么没有引用 Aspire 也会执行该文件

这里的 Aspire 指 Visual Studio Web Tools 安装目录中的设计时工具,与项目是否直接引用 .NET Aspire NuGet package 无关。

加载 Web 项目时,Visual Studio 可以从自身安装目录导入类似下面的文件:

MSBuild\Microsoft\VisualStudio\WebTools.Aspire\Microsoft.WebTools.Aspire.targets

文件中的目标带有:

BeforeTargets="CompileDesignTime"

因此,它主要参与 IDE 的设计时编译。只要导入链产生了 @(JsonSchemaSegment),以下条件就会成立:

Condition=" '@(JsonSchemaSegment)' != '' "

随后,Web Tools 会尝试生成:

CombinedComponentSchema.json
AppSettingsSchema.json

排查时应确认以下几个值在失败时具体指向哪些文件:

@(JsonSchemaSegment)
$(AppSettingJsonSchemaLoadFilePath)
$(JsonSchemaCombinedFilePath)
$(AppSettingsJsonSchemaCombinedFilePath)

XML 验证为什么不能说明问题

确认 Microsoft.WebTools.Aspire.targets 是有效 XML,只能说明 MSBuild 可以读取该文件。当前错误来自 JSON 解析器,所以需要检查 task 当作 JSON 读取的输入文件。

LineNumber: 2 | BytePositionInLine: 0 是 JSON 解析器报告的位置。输入文件的这个位置出现 <,通常有以下几种原因:

  • 输入实际是 XML 文件;
  • 文件扩展名是 .json,内容却被错误写成了 XML;
  • NuGet 源、代理或认证服务返回了 HTML/XML 错误页;
  • obj 中的合并 Schema 是上次失败后留下的损坏文件;
  • @(JsonSchemaSegment) 意外包含了 .targets、.props 或其他非 JSON 文件。

单击错误后跳转到 .targets 文件,只能说明这里是 task 的调用位置,无法证明 JSON 解析器读取的就是这个 .targets 文件。

先确认 Azure.Identity 的版本关联

在同一台机器、同一个 commit 和相同 SDK 下进行一次最小对照。先使用:

<ItemGroup>
  <PackageReference Include="Azure.Identity" Version="1.19.x" />
</ItemGroup>

清理、还原并构建后,再改回:

<ItemGroup>
  <PackageReference Include="Azure.Identity" Version="1.20.0" />
</ItemGroup>

这里的 1.19.x 代表已经确认正常的 1.20.0 之前版本。测试时应替换为项目实际验证过的完整版本号,不要在正式项目中直接使用通配版本。

如果项目使用中央包管理,请修改 Directory.Packages.props:

<ItemGroup>
  <PackageVersion Include="Azure.Identity" Version="已验证的完整版本号" />
</ItemGroup>

每次切换版本后,都要清除项目生成目录和 NuGet 缓存,以免旧文件干扰对照结果。

生成 MSBuild 诊断日志

关闭 Visual Studio,然后在项目目录中执行:

dotnet build .\Project.csproj -bl:azure-identity.binlog -v:diag

再生成展开后的项目:

dotnet msbuild .\Project.csproj /pp:expanded-project.xml

在诊断日志和 expanded-project.xml 中搜索:

JsonSchemaSegment
AppSettingJsonSchemaLoadFilePath
GenerateCombinedComponentSchema
GenerateCombinedAppSettingsSchema
Microsoft.WebTools.Aspire.targets
Azure.Identity

可以使用 MSBuild Structured Log Viewer 打开二进制日志。查看 GenerateCombinedComponentSchemaTarget 和 GenerateCombinedAppSettingsSchemaTarget 执行前收到的参数,不要只看最后一条异常。

需要逐项确认:

  1. @(JsonSchemaSegment) 包含哪些完整路径;
  2. 这些路径是否存在;
  3. 对应文件是否确实为 JSON;
  4. AppSettingJsonSchemaLoadFilePath 是否错误指向 XML、HTML 或 .targets 文件;
  5. CombinedComponentSchema.json 和 AppSettingsSchema.json 的内容是否完整;
  6. 相关 item 是由哪个 .props、.targets 或 NuGet package 加入的。

还可以列出直接依赖和传递依赖:

dotnet list .\Project.csproj package --include-transitive

较新的 SDK 可能会提示改用 dotnet package list,使用当前 SDK 支持的形式即可。

检查实际输入文件

从日志中找到完整路径后,直接查看文件开头。PowerShell 示例:

Get-Content "日志中显示的文件完整路径" -TotalCount 10

有效的 JSON Schema 通常以 { 开始,例如:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema"
}

如果文件以以下任一内容开头,就找到了错误输入:

<?xml version="1.0"?>
<!DOCTYPE html>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

如果扩展名是 .json,内容却是登录页、403/404 页面或代理返回的信息,还应检查企业代理、私有 NuGet 源和凭据状态。如果输入路径直接指向 .targets,请在展开后的项目中继续追查是谁将该路径加入了 @(JsonSchemaSegment)。

清理本机状态

如果只有一名开发人员遇到问题,应着重检查本机缓存和 Visual Studio 组件版本。关闭所有 Visual Studio 实例后执行:

dotnet nuget locals all --clear
dotnet clean .\Project.csproj
dotnet restore .\Project.csproj --force --no-cache
dotnet build .\Project.csproj -bl:after-clean.binlog

同时删除受影响项目的以下生成目录:

bin
obj
.vs

这些目录通常不应包含需要保留的源文件。删除前仍要确认项目没有误将手工文件放在其中。

完成后重新打开 solution。NuGet assets、合并 Schema 和 Visual Studio 的 solution 缓存会重新生成。系统重装后问题曾暂时消失,符合缓存或组件状态恢复正常、依赖重新还原后问题再次出现的现象,但这一现象还不能证明根因。

可用的解决办法

1. 暂时锁定已验证可用的 Azure.Identity 版本

如果对照测试确认问题只会由 1.20.0 或更高版本触发,可以暂时锁定到团队已验证的较低版本,并保留两个版本的 binlog。

降级也会失去新版本中的修复和功能。使用前应确认项目是否依赖新 API,以及旧版本能否满足安全和兼容性要求。

2. 统一 Visual Studio、SDK 和 NuGet 环境

比较正常机器与故障机器上的以下输出:

dotnet --info
dotnet --list-sdks
dotnet nuget list source

还要比较以下环境信息:

  • Visual Studio 的完整版本和更新通道;
  • ASP.NET and web development workload;
  • 已安装的 .NET SDK;
  • NuGet.Config;
  • global.json;
  • 环境变量和企业代理配置;
  • 实际解析出的传递依赖版本。

即使两台机器都使用最新版,环境也未必一致。预览通道、更新批次、SDK feature band 或 workload 组件不同,都可能导致设计时构建使用不同版本的 task。

如果怀疑 Visual Studio 文件损坏,应先通过 Visual Studio Installer 执行 Repair,不要手工编辑或替换:

Microsoft.WebTools.Aspire.targets
Microsoft.WebTools.Aspire.MSBuild.dll

这些文件属于 Visual Studio 的安装内容。手工修改可能影响所有 Web 项目,之后也会被更新或修复操作覆盖。

3. 仅在日志确认后排除错误的 Schema item

如果 binlog 已确认某个非 JSON 文件被错误加入 @(JsonSchemaSegment),可以在项目或 Directory.Build.targets 中有针对性地临时排除。例如:

<Project>
  <Target Name="RemoveInvalidJsonSchemaSegment"
          BeforeTargets="GenerateCombinedComponentSchemaTarget">
    <ItemGroup>
      <JsonSchemaSegment Remove="C:\完整路径\错误输入文件.targets" />
    </ItemGroup>
  </Target>
</Project>

路径必须取自实际日志,不能照抄示例。也不要无条件删除全部 @(JsonSchemaSegment),否则合法的 appsettings Schema 功能可能失效。请为这个 workaround 添加注释,并在 Visual Studio 或相关 package 修复后将其移除。

如果错误输入来自某个 NuGet package 的 build 或 buildTransitive 资产,更合适的临时措施是锁定 package 版本并提交可复现日志,避免长期覆盖 Visual Studio 的目标。

恢复 IEnumerable 的着色和悬停提示

解决设计时构建错误后,清理 obj 和 .vs,再重新加载 solution。可以按以下顺序操作:

  1. 关闭 Visual Studio;
  2. 删除受影响项目的 bin、obj 和 solution 的 .vs;
  3. 清除 NuGet 缓存并重新 restore;
  4. 确认命令行构建不再出现 Schema 错误;
  5. 重新打开 Visual Studio,等待依赖还原和项目分析完成;
  6. 如有必要,卸载并重新加载受影响项目,或者重启 Visual Studio。

如果构建错误已经消失,IEnumerable 仍没有语义着色和 Quick Info,请继续检查:

  • 文件是否被当作普通文本打开;
  • 项目是否处于 unloaded 或 incompatible 状态;
  • System.Collections.Generic 所在目标框架的引用是否解析成功;
  • Error List 中是否还存在 design-time build 或 language service 错误;
  • 关闭第三方扩展后,问题是否仍然存在。

这个问题不需要为 IEnumerable 添加特殊引用。项目模型、框架引用和 Roslyn 设计时构建恢复正常后,它的语义着色与悬停说明通常也会恢复。

提交问题时应附带什么

如果清理缓存后仍能稳定复现,建议准备以下材料:

  • 可以复现问题的最小项目;
  • 正常版本和异常版本各自生成的 binlog;
  • dotnet --info 输出;
  • Visual Studio 完整版本;
  • dotnet list package --include-transitive 输出;
  • 展开项目中 JsonSchemaSegment 的来源;
  • 被错误读取文件的路径和前几行内容;
  • 正常机器与故障机器之间的环境差异。

在获得这些信息之前,更准确的描述是:“升级 Azure.Identity 后,可以重复触发 Web Tools 的 JSON Schema 生成错误。”错误指向 Microsoft.WebTools.Aspire.targets,并不足以证明该 XML 文件已经损坏,也不能证明 Azure.Identity 直接将 MSBuild XML 当作 JSON 解析。

备注:内容仅供参考。