Skip to content

Make telemetry initialization and disposal best-effort so it can never fail a build - #14554

Draft
AlesProkop with Copilot wants to merge 2 commits into
mainfrom
copilot/nre-crash-fix
Draft

Make telemetry initialization and disposal best-effort so it can never fail a build#14554
AlesProkop with Copilot wants to merge 2 commits into
mainfrom
copilot/nre-crash-fix

Conversation

Copilot AI commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Context

MSBuild 18.8.2 on .NET Framework fails builds with MSBUILD : error MSB1025 when a NullReferenceException escapes VsTelemetryInitializer.Dispose(). Two independent defects combine here:

  1. TelemetryManager swallowed the wrong exceptions. Both init and dispose used a filtered catch (FileNotFoundException/FileLoadException/TypeLoadException), which doesn't match an NRE thrown from inside the VS telemetry stack. The exception propagated up through BuildManager.Dispose() to MSBuildApp.Execute and failed the build.

  2. BuildManager disposed a session it doesn't own. The telemetry session is process-wide, created by the entry point (XMake.Main) or the host (VS). MSBuildApp.BuildProject's finally disposes BuildManager.DefaultBuildManager, so the session was torn down while the process was still running — leaving CrashTelemetryRecorder.PostFaultEvent posting to a disposed session, which is the second stack trace in the report.

Telemetry is best-effort infrastructure; no failure inside it should be observable by a build.

Changes Made

src/Framework/Telemetry/TelemetryManager.cs

  • TryInitializeTelemetry and Dispose now use a catch-all instead of a type-filtered catch. Comments record which exceptions are expected and why the filter isn't sufficient.
  • s_disposed is set before the teardown work, and DefaultActivitySource is cleared, so nothing can emit through a source backed by a disposed session.
  • VsTelemetryInitializer.Initialize no longer dereferences the session unconditionally. CreateAndGetDefaultSession can return null (e.g. telemetry disabled machine-wide), so UseVsIsOptedIn()/Start() and ownership are only taken when a session actually exists:
session = TelemetryService.CreateAndGetDefaultSession(CollectorApiKey);

if (session is not null)
{
    session.UseVsIsOptedIn();
    session.Start();
    s_ownsSession = true;
}
  • VsTelemetryInitializer.Dispose snapshots and clears the static state before disposing, so a throwing Dispose() can't leave a stale session behind and a repeat call is a no-op.
  • Dropped using System; / using System.IO;, now unused (IDE0005 is warning-as-error in official builds).

src/Build/BackEnd/BuildManager/BuildManager.cs

  • Removed TelemetryManager.Instance.Dispose() from Dispose(bool). This is a no-op change for hosts, which initialize with isStandalone: false and therefore never owned the session; XMake.Main and MSBuild.Coordinator/Program.cs already pair their own Initialize/Dispose, and on .NET Core Dispose() has no body.

Testing

  • New src/Framework.UnitTests/TelemetryManager_Tests.cs: disposal without initialization doesn't throw, disposal is idempotent, ResetForTest clears state.
  • New BuildManagerDisposeDoesNotDisposeProcessWideTelemetry in src/Build.UnitTests/Telemetry/Telemetry_Tests.cs, verified to fail when the removed TelemetryManager.Instance.Dispose() call is restored.

Notes

This is defensive. The underlying reason TelemetrySession.Dispose() NREs in the reporter's environment (Windows Server 2022, VS 2026 + VS 2022 side-by-side, VS_TELEMETRY_OPT_OUT=1) is in the closed-source VS Telemetry SDK and is not addressed here — but no failure mode inside it can fail a build anymore.

Worth a look during review: BuildManager.EndBuildTelemetry() calls StartActivity without a guard. Left alone as out of scope, but it's adjacent.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Hello @copilot, I noticed that you’re changing an .swr file or any file under src/Package/MSBuild.VSSetup.. Please make sure to validate this change by an experimental VS insertion. This is accomplished by pushing to an exp/* branch, which requires write permissions to this repo.

Fixes the MSB1025 build failure caused by a NullReferenceException escaping
VsTelemetryInitializer.Dispose(), and stops BuildManager.Dispose() from tearing
down the process-wide telemetry session it does not own.

Co-authored-by: AlesProkop <276576870+AlesProkop@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix unhandled exception in VsTelemetryInitializer.Dispose Make telemetry initialization and disposal best-effort so it can never fail a build Jul 28, 2026
Copilot AI requested a review from AlesProkop July 28, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Unhandled Exception]: VsTelemetryInitializer.Dispose(): System.NullReferenceException: Object reference not set to an instance of an object.

2 participants