SnowDriven Research · Reference
.NET 8 to .NET 10: the breaking changes that hit web apps
Microsoft’s six breaking-change lists between .NET 8 and .NET 10 have 194 entries. We read every one for an ASP.NET Core web app, an API or a worker service: 6 are likely to need attention, 33 are possible and 31 are rare. Each links to Microsoft’s own page, checked on 25 October 2026.
- For
- .NET teams and their leads
- Lists read
- Six, from Microsoft
- Entries listed
- 194
- On this page
- 70
- Checked
- 25 Oct 2026
The short answer
The 6 changes we rate likely for a typical web app or worker. Each has its entry below.
URI query and fragment redaction in IHttpClientFactory logs
.NET 9.
IHttpClientFactory’s default logging now replaces the URI query with*and removes user info and fragments in messages passed toILogger.Default .NET images use Ubuntu
.NET 10. Tags without an explicit OS (for example
sdk:10.0) now point to Ubuntu 24.04 “Noble” instead of Debian, and Debian-based .NET 10 images aren’t shipped.'dotnet restore' audits transitive packages
.NET 10. For projects targeting .NET 10 or later,
NuGetAuditModedefaults to “all”, so known-vulnerable transitive packages raise warnings, which can fail restore when warnings are errors.Deprecation of WithOpenApi extension method
ASP.NET Core 10.
WithOpenApiis deprecated; calling it produces warningASPDEPR002, though the call still compiles and runs.Exception is thrown when applying migrations if there are pending model changes
EF Core 9. If the model differs from the last migration’s snapshot,
dotnet ef database update,MigrateandMigrateAsyncnow throw instead of silently skipping the changes.Exception is thrown when applying migrations in an explicit transaction
EF Core 9.
MigrateandMigrateAsyncnow manage their own transaction and execution strategy, so calling them inside a user-started transaction throws.
Before you change anything. This page is general information, not advice for your system. Each entry summarizes Microsoft’s own page, checked on 25 October 2026. Microsoft can change its lists, and it says the .NET 10 list is a work in progress and not complete, so read the linked page before you act on an entry. Whether a change is likely, possible or rare for you is our judgement for a typical app, not Microsoft’s. Back up, test every change on a copy that isn’t in production, keep a way back, and follow your own change process. You follow these steps at your own risk (terms). SnowDriven isn’t affiliated with Microsoft (trademarks).
How to use this list
The upgrade steps (SDK, retarget, packages, images, test) are in our .NET 8 end-of-support guide. This page is the part of that work Microsoft’s lists leave unsorted.
Start with the likely ones
They’re the ones a typical app is most likely to meet: two are about container images and restore, two about EF Core migrations, one about logging and one about a template call.
Search for the possible ones
Each entry’s “How to tell” names what to search your solution for: an API, a setting, a package or a warning code. No match, no work.
Build with warnings visible
Many entries are new compiler or restore warnings (codes starting
ASPDEPR,SYSLIBorNU). WithTreatWarningsAsErrorson, each one fails the build, so decide case by case whether to fix it or exempt it in writing.Then test the behavior
Behavioral changes compile cleanly and show up at runtime, in logs, or in what a client receives. Run the workflows the business depends on, on a staging copy, before the first production deployment.
Already on the latest .NET 8 patch?
Microsoft shipped 4 of these changes in .NET 8 servicing releases too, so an app on that patch or later already behaves this way:
Support for .NET 8 and .NET 9 ends on 10 November 2026. What that means, how to check what you run and the upgrade path: .NET 8 and 9 end of support.
Likely (6)
Changes most web apps or workers moving from .NET 8 will meet. Check each one.
URI query and fragment redaction in IHttpClientFactory logs
IHttpClientFactory’s default logging now replaces the URI query with*and removes user info and fragments in messages passed toILogger.- How to tell
- Outgoing HTTP request log messages from
IHttpClientFactoryno longer show query strings; log queries, alerts or dashboards keyed on them. - What to do
- If logging queries is safe, set the
System.Net.Http.DisableUriRedactionAppContextswitch (csproj,runtimeconfig.json, orDOTNET_SYSTEM_NET_HTTP_DISABLEURIREDACTION). - Why it’s likely (our view)
- Most server apps use
IHttpClientFactorywith default logging, so log output changes visibly, though app behavior doesn’t.
Default .NET images use Ubuntu
Tags without an explicit OS (for example
sdk:10.0) now point to Ubuntu 24.04 “Noble” instead of Debian, and Debian-based .NET 10 images aren’t shipped.- How to tell
- Dockerfiles using
mcr.microsoft.com/dotnet/*:10.0tags without an OS suffix, or Debian-specific tags or packages. - What to do
- Test with Ubuntu-based images; if you need Debian, build and maintain your own image (Microsoft documents installing .NET in a Dockerfile).
- Why it’s likely (our view)
- Nearly every Linux container user moving to 10.0 tags gets a new base distro; Microsoft says most users won’t be affected.
'dotnet restore' audits transitive packages
For projects targeting .NET 10 or later,
NuGetAuditModedefaults to “all”, so known-vulnerable transitive packages raise warnings, which can fail restore when warnings are errors.- How to tell
- New
NU1901-NU1904warnings during restore; restore failures withTreatWarningsAsErrors;NuGetAuditModeproperty. - What to do
- Upgrade or promote vulnerable transitive packages; or use
WarningsNotAsErrorsforNU1901-NU1904,NuGetAuditSuppress, orNuGetAuditMode=direct. - Why it’s likely (our view)
- Most real dependency graphs contain some flagged transitive package, and many CI builds treat warnings as errors.
Deprecation of WithOpenApi extension method
WithOpenApiis deprecated; calling it produces warningASPDEPR002, though the call still compiles and runs.- How to tell
- Warning
ASPDEPR002on.WithOpenApi()calls on minimal API endpoints. - What to do
- Remove
.WithOpenApi(); useAddOpenApiOperationTransformer(Microsoft.AspNetCore.OpenApi),IOperationFilter(Swashbuckle) orIOperationProcessor(NSwag). - Why it’s likely (our view)
- The .NET 8 minimal API template adds
.WithOpenApi()to its sample endpoint, so many APIs started from it carry the call. It’s a warning, but it fails builds that treat warnings as errors.
Exception is thrown when applying migrations if there are pending model changes
If the model differs from the last migration’s snapshot,
dotnet ef database update,MigrateandMigrateAsyncnow throw instead of silently skipping the changes.- How to tell
- Exception
The model for context ... has pending changes. Add a new migration before updating the database.(RelationalEventId.PendingModelChangesWarning); common cause:DateTime.Now/Guid.NewGuid()inHasData. - What to do
- Add a migration (or remove the
Migratecall if you don’t use migrations); makeHasDatavalues static; suppressPendingModelChangesWarningonly for false positives. - Why it’s likely (our view)
- Microsoft rates it High; any app that applies migrations at startup or in CI can hit it.
Exception is thrown when applying migrations in an explicit transaction
MigrateandMigrateAsyncnow manage their own transaction and execution strategy, so calling them inside a user-started transaction throws.- How to tell
- Error for warning
Microsoft.EntityFrameworkCore.Migrations.MigrationsUserTransactionWarning:A transaction was started before applying migrations...; code wrappingMigrateAsyncinBeginTransactionAsync. - What to do
- Remove the external transaction and execution strategy; if one is truly needed and concurrency is handled, ignore
MigrationsUserTransactionWarning. - Why it’s likely (our view)
- Microsoft rates it High and calls the wrapped-transaction pattern “commonly used”; only apps using that pattern are hit.
Possible (33)
Changes that hit some web apps, depending on what the code uses. Search your solution for what’s under “How to tell”.
.NET runtime, libraries and SDK · 17
Environment variables take precedence in app runtime configuration settingsWhen an environment variable and
runtimeconfig.json(or project file) set the same runtime setting, the environment variable now wins. Previouslyruntimeconfig.jsontook precedence.- How to tell
DOTNET_*runtime-config environment variables (Microsoft’s example:DOTNET_gcServer) set in the container, App Service settings or host that conflict withruntimeconfig.json/csproj values such asSystem.GC.Server.- What to do
- Unset the environment variable or set it to the value you actually want.
- Why it’s possible (our view)
- Hosting platforms and Dockerfiles often set
DOTNET_*variables that were silently ignored before and now take effect.
In-box BinaryFormatter implementation removed and always throwsThe in-box
BinaryFormatternow throws on use, even with the settings that previously enabled it; those settings are removed.- How to tell
- Any use of
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter, directly or through a dependency (for example legacy caching or session code); exceptions at runtime. - What to do
- Move to a different serialization format (see the
BinaryFormattermigration guide). An unsupported separate NuGet package exists if you accept the risk. - Why it’s possible (our view)
- Rare in new code, but legacy caches, session state and older libraries still use it, and it fails at runtime, not compile time.
HttpClientFactory uses SocketsHttpHandler as primary handlerThe default primary handler for
IHttpClientFactoryclients is nowSocketsHttpHandler, withPooledConnectionLifetimematchingHandlerLifetime. Casting it toHttpClientHandlerthrowsInvalidCastException.- How to tell
- Code that casts the default primary handler, for example
ConfigurePrimaryHttpMessageHandler((h, _) => ((HttpClientHandler)h)…); anInvalidCastExceptionsayingSocketsHttpHandlercan’t be cast toHttpClientHandler. - What to do
- Explicitly configure a primary handler per client or via
ConfigureHttpClientDefaults, or check for bothHttpClientHandlerandSocketsHttpHandler. - Why it’s possible (our view)
- Only code that casts the default primary handler (for example, to set client certificates, cookies or a proxy) breaks.
HttpClient metrics report server.port unconditionally
http.client.request.duration,http.client.connection.durationandhttp.client.open_connectionsnow always reportserver.port, including default ports 80/443, which can break existing monitoring queries.- How to tell
- Prometheus or other monitoring queries over
HttpClientmetrics that assumeserver.portis absent for ports 80/443. - What to do
- No action unless you use these
HttpClientmetrics; if you do, update monitoring queries such as Prometheus queries. - Why it’s possible (our view)
- Only affects teams that export
HttpClientmetrics (for example via OpenTelemetry) and query onserver.port.
API obsoletions with non-default diagnostic IDs (.NET 9)New obsoletion warnings
SYSLIB0009,SYSLIB0014(ServicePointManagerfully obsolete),SYSLIB0054toSYSLIB0056andSYSLIB0057(X509Certificate2binary/file constructors) can’t be suppressed viaCS0618.- How to tell
- Build warnings
SYSLIB0009,SYSLIB0014,SYSLIB0054,SYSLIB0055,SYSLIB0056,SYSLIB0057; build failures ifTreatWarningsAsErrorsis on. - What to do
- Follow each diagnostic’s linked guidance; suppress, if needed, using the specific
SYSLIBID rather thanCS0618. - Why it’s possible (our view)
ServicePointManagerandX509Certificate2file constructors are still common in older server code; impact is build warnings unless warnings are errors.
Some X509Certificate2 and X509Certificate constructors are obsolete
X509Certificate/X509Certificate2constructors takingbyte[],ReadOnlySpan<byte>or a file path, andX509Certificate2Collection.Import, are obsolete and produce warningSYSLIB0057.- How to tell
- Warning
SYSLIB0057on newX509Certificate2(path/bytes, ...)orX509Certificate2Collection.Importcalls. - What to do
- Follow the guidance on Microsoft’s
SYSLIB0057page, which the change links to. - Why it’s possible (our view)
- Loading certificates from files or bytes (TLS client certificates, signing keys) is common in server apps; it’s a compile-time warning.
Ambiguous overload resolution affecting StringValues implicit operatorsNew
params ReadOnlySpanoverloads ofString.Concat,String.Join,Path.Combine,Path.JoinandStringBuilder.AppendJoinmake calls that passStringValuesambiguous, so the compiler reportsCS0121.- How to tell
- Compiler error
CS0121(“The call is ambiguous …”) onString.Join,String.Concat,Path.Combine,Path.JoinorStringBuilder.AppendJoincalls involvingStringValues. - What to do
- Cast arguments to the intended type or use named parameters so the intended overload is selected.
- Why it’s possible (our view)
StringValuesis common in header and query handling code in web apps; the break shows up at compile time.
BackgroundService runs all of ExecuteAsync as a Task
BackgroundServicenow runs all ofExecuteAsyncon a background thread; the synchronous part before the first await no longer runs during startup or blocks other services.- How to tell
BackgroundService.ExecuteAsyncimplementations whose code before the first await must finish before other hosted services or the app start.- What to do
- Move startup-critical work to the constructor or an overridden
StartAsync(beforebase.StartAsync), or implementIHostedLifecycleService. - Why it’s possible (our view)
- Many workers subclass
BackgroundService; only those relying on the synchronous prefix’s startup ordering break.
Null values preserved in configurationThe configuration binder now binds explicit null values (including array elements and empty arrays) instead of skipping them; nulls on non-nullable value types become default(T).
- How to tell
appsettings.json(or other provider) entries set to null; options properties that used to keep their defaults now become null or default.- What to do
- With JSON, replace null with "" to restore the old result; for other providers, remove null entries from configuration.
- Why it’s possible (our view)
- Explicit nulls in appsettings files are fairly common and silently change bound options values.
Message no longer duplicated in Console log outputWith the JSON console formatter,
Messagenow typically appears only at the top level, not repeated inside theStateobject.- How to tell
- Log pipelines that parse JSON console logs (
AddJsonConsole) and readMessagefrom theStateobject. - What to do
- Read the top-level
Messagefield instead of the one insideState. - Why it’s possible (our view)
- Containerized apps often ship JSON console logs to collectors; only parsers that read
State.Messagebreak.
Default trace context propagator updated to W3C standard
DistributedContextPropagator’s default is now theW3Cpropagator: it uses the baggage header instead of Correlation-Context, enforcesW3Cencoding, and supports onlyW3C-format parent IDs.- How to tell
- Services exchanging Correlation-Context headers or non-
W3C(hierarchical) trace parent IDs with other systems;DistributedContextPropagator.Current. - What to do
- To keep the legacy behavior, set
DistributedContextPropagator.Current = DistributedContextPropagator.CreatePreW3CPropagator(). - Why it’s possible (our view)
- Matters for distributed systems that interoperate with older services using legacy correlation headers.
System.Linq.AsyncEnumerable in .NET 10.NET 10 includes
AsyncEnumerablewith LINQ forIAsyncEnumerable<T>, replacing the communitySystem.Linq.Asyncpackage and potentially causing ambiguity compile errors.- How to tell
- Direct or transitive references to the
System.Linq.Asyncpackage; ambiguous-call compile errors onIAsyncEnumerableLINQ methods. - What to do
- Remove
System.Linq.Asyncor upgrade it to 7.0.0; for transitive references useExcludeAssets=compile(orall). - Why it’s possible (our view)
System.Linq.Asyncis a widely used dependency, often pulled in transitively.
C# 14 overload resolution with span parametersC# 14 span conversions make span-based methods (for example
MemoryExtensions) bind in more places, including Expression lambdas, which then fail at runtime when compiled with interpretation.- How to tell
- Expression trees containing calls like
array.Contains(x)that now bind toMemoryExtensions.Contains; runtime exceptions fromExpression.Compilewith interpretation. - What to do
- Force non-span overloads: cast to the exact parameter type (for example
IEnumerable<T>), useAsEnumerable(), or callEnumerablemethods statically. - Why it’s possible (our view)
- Expression-tree code (dynamic filters, rules engines) is common; impact depends on interpretation being used.
API obsoletions with non-default diagnostic IDs (.NET 10)New obsoletion warnings:
SYSLIB0058(SslStreamcipher/hash properties),SYSLIB0059,SYSLIB0060(Rfc2898DeriveBytesconstructors),SYSLIB0061(Queryable.MaxByandMinBywithIComparer<TSource>),SYSLIB0062(XsltSettings.EnableScript).- How to tell
- Build warnings
SYSLIB0058-SYSLIB0062, for example on newRfc2898DeriveBytes(...); build failures ifTreatWarningsAsErrors. - What to do
- Follow each diagnostic’s guidance (for example, use
Rfc2898DeriveBytes.Pbkdf2,SslStream.NegotiatedCipherSuite); suppress only by the specificSYSLIBID. - Why it’s possible (our view)
Rfc2898DeriveBytesconstructors are common in password-hashing code; it’s a compile-time warning.
MailAddress enforces validation for consecutive dots
MailAddressnow throwsFormatExceptionfor addresses with consecutive dots in the local or domain part (for exampletest..address@example.com), per RFC 5322/2822.- How to tell
- new
MailAddress(...)calls;FormatExceptionfor previously accepted addresses in sign-up, import or email-sending code. - What to do
- Make sure such addresses aren’t passed, or handle the
FormatException(for example with try/catch or prior validation). - Why it’s possible (our view)
- Web apps commonly parse user-supplied email addresses with
MailAddress; stored legacy data may contain such addresses.
System.Text.Json checks for property name conflicts
System.Text.Jsonnow throwsInvalidOperationExceptionearly when user properties conflict with metadata names ($type, $id, $ref or custom discriminator names) under polymorphism or reference preservation.- How to tell
InvalidOperationException:The type ... contains property ... that conflicts with an existing metadata property namewhen serializing polymorphic or reference-preserving types.- What to do
- Rename conflicting properties, or apply
[JsonIgnore]if the property must stay. - Why it’s possible (our view)
- Polymorphic DTOs with their own “Type”-style discriminator property are a common pattern in APIs.
NU1510 is raised for direct references pruned by NuGetWith pruning enabled, projects targeting .NET 10+ get warning
NU1510for direct package references that the framework already provides (unnecessary references).- How to tell
- Warning
NU1510onPackageReferenceitems such asSystem.Text.Json; build failures if warnings are errors. - What to do
- Remove the unnecessary reference, or for multi-targeting include it conditionally only for frameworks that need it.
- Why it’s possible (our view)
- Upgraded apps often carry explicit references to framework-provided packages left over from older targets.
ASP.NET Core · 9
Forwarded headers middleware ignores X-Forwarded-* headers from unknown proxiesForwarded headers middleware now processes X-Forwarded-* headers only from proxies listed in
KnownProxies/KnownNetworks; headers from unknown sources are ignored.- How to tell
UseForwardedHeadersbehind a TLS-terminating proxy or load balancer; symptoms Microsoft names: infinite HTTPS redirects, or authentication failing when an HTTPS request is expected.- What to do
- Add every legitimate proxy to
KnownProxiesorKnownNetworks; clearing both lists orASPNETCORE_FORWARDEDHEADERS_ENABLED=truerestores old behavior (not recommended). - Why it’s possible (our view)
- Common for apps behind a proxy or load balancer, but it shipped in .NET 8.0.17, so an up-to-date .NET 8 app already has it.
HostBuilder enables ValidateOnBuild/ValidateScopes in development environmentIn the Development environment,
HostBuildernow turns onValidateOnBuildandValidateScopesunlessUseDefaultServiceProvidersets them, so DI registration problems surface at startup.- How to tell
- DI validation errors at startup when running in Development (affected APIs:
HostBuilder,HostBuilder.Build()). - What to do
- Fix the reported DI registration problem; if you can’t, disable validation with
UseDefaultServiceProvider. - Why it’s possible (our view)
- Only hits apps with latent DI lifetime/registration errors, and only in Development.
Middleware types with multiple constructorsIf the DI container doesn’t implement
IServiceProviderIsService, middleware with several satisfiable constructors now throws instead of picking the one with most parameters.- How to tell
InvalidOperationExceptionMultiple constructors accepting all given argument types have been found...; Microsoft namesCookiePolicyMiddlewarewithAutofac.Extensions.DependencyInjection7.x.- What to do
- Upgrade to a DI container implementing
IServiceProviderIsService, or putActivatorUtilitiesConstructorAttributeon the preferred middleware constructor. - Why it’s possible (our view)
- Only apps using a third-party DI container (Microsoft names Autofac 7.x); the default container is unaffected.
Exception diagnostics suppressed when TryHandleAsync returns trueWhen an
IExceptionHandler.TryHandleAsyncreturns true, the exception handler middleware no longer logs the exception, emits theHandledExceptionevent, or addserror.typeto the duration metric.- How to tell
- Apps with
IExceptionHandlerimplementations noticing handled exceptions vanish from logs/telemetry (UnhandledExceptionlog,http.server.request.durationerror.typetag). - What to do
- If you still want telemetry for handled exceptions, set
ExceptionHandlerOptions.SuppressDiagnosticsCallbackto return false. - Why it’s possible (our view)
- Only apps using
IExceptionHandler(added in .NET 8) and relying on middleware-emitted logs/metrics.
IActionContextAccessor and ActionContextAccessor are obsolete
IActionContextAccessorandActionContextAccessorare marked obsolete (ASPDEPR006); endpoint metadata is available viaHttpContext.GetEndpoint().- How to tell
- Warning
ASPDEPR006whereverIActionContextAccessor/ActionContextAccessoris injected or registered (often alongsideIUrlHelperFactoryin MVC apps). - What to do
- Inject
IHttpContextAccessorand read metadata viaHttpContext.GetEndpoint(), for exampleGetMetadata<ActionDescriptor>(). - Why it’s possible (our view)
- Seen in older MVC codebases; warning only. The overview labels it
Source incompatible/behavioral changebut the change page itself says only source compatibility.
IPNetwork and ForwardedHeadersOptions.KnownNetworks are obsolete
Microsoft.AspNetCore.HttpOverrides.IPNetworkandForwardedHeadersOptions.KnownNetworksare obsolete (ASPDEPR005) in favor ofSystem.Net.IPNetworkandKnownIPNetworks.- How to tell
- Warning
ASPDEPR005onKnownNetworksorMicrosoft.AspNetCore.HttpOverrides.IPNetworkin forwarded headers configuration. - What to do
- Switch to
System.Net.IPNetworkand theKnownIPNetworksproperty. - Why it’s possible (our view)
- Apps behind proxies that configure
KnownNetworks; warning only.
Razor runtime compilation is obsoleteRazor runtime compilation APIs are obsolete (
ASPDEPR003); Microsoft recommends build-time compilation for production and Hot Reload for development.- How to tell
- Warning
ASPDEPR003onAddRazorRuntimeCompilation(and theMicrosoft.AspNetCore.Mvc.Razor.RuntimeCompilationtypes). - What to do
- Remove
AddRazorRuntimeCompilationcalls and use Hot Reload instead. - Why it’s possible (our view)
- Common in MVC/Razor Pages apps for dev-time view editing; warning only.
WebHostBuilder, IWebHost, and WebHost are obsolete
WebHostBuilder(ASPDEPR004) andIWebHost/WebHost(ASPDEPR008) are obsolete;HostBuilderandWebApplicationBuilderare where future investment goes.- How to tell
- Warnings
ASPDEPR004/ASPDEPR008on newWebHostBuilder(),WebHost.CreateDefaultBuilder,IWebHost, including test code building aTestServerfrom aWebHostBuilder. - What to do
- Migrate to
HostBuilderwithConfigureWebHost(usehost.GetTestServer()for tests) or toWebApplicationBuilder. - Why it’s possible (our view)
- Apps carried forward from ASP.NET Core 2.x and
TestServer-based tests; apps onWebApplicationorHostbuilders are unaffected. Warning only.
EF Core · 7
EF tools now require framework to be specified for multi-targeted projects
dotnet-efcommands on projects using<TargetFrameworks>(multiple TFMs) now fail unless--frameworkis specified.- How to tell
- Error
The project targets multiple frameworks. Use the --framework option to specify which target framework to use. - What to do
- Pass
--framework(for example--framework net10.0) to every EF tools command on multi-targeted projects. - Why it’s possible (our view)
- Microsoft rates it Medium, but only multi-targeted projects; most web apps target a single framework.
Parameterized collections now use multiple parameters by defaultLINQ
Containsover in-memory collections now translates to multiple scalar parameters (IN (@ids1, @ids2, …)) instead of one JSON array parameter; performance can differ.- How to tell
- Queries like
ids.Contains(b.Id)generating different SQL; performance regressions, especially with large collections. - What to do
- If regressions appear, set
UseParameterizedCollectionModeglobally, or useEF.Parameter/EF.Constantper query. - Why it’s possible (our view)
- Microsoft rates it Low and usually harmless, but Contains-over-list queries are very common, so worth load-testing.
SQL parameter names are now simplifiedGenerated SQL parameter names drop the
__prefix and counter (@__city_0becomes@city); queries are functionally identical.- How to tell
- Snapshot tests comparing SQL text, interceptors/loggers parsing
CommandTextorParameterName; a query-plan recompilation spike after deployment. - What to do
- Update code or tests depending on exact parameter names; large systems should plan for temporary plan recompilation after deploy.
- Why it’s possible (our view)
- Microsoft rates it Low; touches SQL-text tests and causes a one-off plan-cache churn on busy databases.
SQL Server json data type used by default on Azure SQL and compatibility level 170With
UseAzureSqlor compatibility level 170+, JSON-mapped primitive collections and owned types use the json type instead of nvarchar(max); existing apps get an altering migration.- How to tell
- A new migration altering nvarchar(max) JSON columns to json after upgrading; failing queries such as DISTINCT over JSON arrays.
- What to do
- Set a compatibility level below 170 (for example
UseCompatibilityLevel(160)), or setHasColumnType("nvarchar(max)")on specific properties. - Why it’s possible (our view)
- Microsoft rates it Low; only Azure SQL (
UseAzureSql) or compat level 170 apps using JSON-mapped columns or primitive collections.
SQL Server: Microsoft.Data.SqlClient adds a broker dependency
Microsoft.Data.SqlClient6.1.6, used by the SQL Server provider, depends onMicrosoft.Identity.Client.Broker, which can drastically increase published app size.- How to tell
- Noticeably larger publish output or container images for apps using the EF Core SQL Server provider.
- What to do
- If not using Entra ID authentication, reference
Microsoft.Data.SqlClient7.1.x explicitly to override EF Core’s version. - Why it’s possible (our view)
- Microsoft rates it Low; can enlarge publish output for any SQL Server app, which matters mostly for container image size.
Using GetDateTimeOffset without an offset now assumes UTC
Microsoft.Data.Sqlite10 treats textual timestamps without an offset as UTC (previously local time) when read withGetDateTimeOffset.- How to tell
- SQLite apps reading offset-less text timestamps as
DateTimeOffset, with values shifted by the server’s local UTC offset. - What to do
- Adjust code; as a temporary last resort set the
Microsoft.Data.Sqlite.Pre10TimeZoneHandlingAppContextswitch to true. - Why it’s possible (our view)
- Microsoft rates it High (likely for SQLite apps storing such timestamps), but only apps on SQLite.
Using GetDateTime with an offset now returns value in UTC
Microsoft.Data.Sqlite10 converts textual timestamps that include an offset to UTC and returnsDateTimeKind.UtcfromGetDateTime(previouslyDateTimeKind.Local).- How to tell
- SQLite apps reading offset-bearing text timestamps as
DateTime; values andDateTime.Kindchange from Local to Utc. - What to do
- Adjust code; as a temporary last resort set the
Microsoft.Data.Sqlite.Pre10TimeZoneHandlingAppContextswitch to true. - Why it’s possible (our view)
- Microsoft rates it High (likely for affected SQLite apps), but only apps on SQLite with offset-bearing text timestamps.
Rare (31)
Changes that need a specific feature, platform or pattern. Read the ones for features you use.
.NET runtime, libraries and SDK · 15
.NET 9 container images no longer install zlib.NET 9 container images stop installing zlib, and no longer update it where the base image already has it, because the runtime now statically links zlib-ng.
- How to tell
- Your app or a native dependency loads the system zlib package inside an official .NET container image.
- What to do
- Usually nothing. If the app directly depends on zlib, install it yourself in the Dockerfile with the package manager.
- Why it’s rare (our view)
- The runtime no longer needs zlib; only apps or native libraries that use the system zlib directly are affected.
Nullable JsonDocument properties deserialize to JsonValueKind.NullDeserializing a JSON null into
JsonDocumentnow returns a non-nullJsonDocumentwhoseRootElement.ValueKindisJsonValueKind.Null, instead of null.- How to tell
- Code that deserializes into
JsonDocument(orJsonDocument?properties) and checks the result for null. - What to do
- Update consuming code to expect
JsonValueKind.Nullrather than a null reference. - Why it’s rare (our view)
- Only matters for models that use
JsonDocumentproperties and null-check them.
HttpClientFactory logging redacts header values by defaultIf
RedactLoggedHeadersisn’t called,IHttpClientFactory’s Trace-level request/response header logs now replace all header values with*instead of logging them as-is.- How to tell
- Trace-level
System.Net.Http.HttpClient.*logs showHeader: *; diagnostics that relied on logged header values. - What to do
- Call
RedactLoggedHeaders(per client or viaConfigureHttpClientDefaults) to choose which headers are redacted, preferably with an allow-list. - Why it’s rare (our view)
- Header logging is Trace level, which few production apps enable; the effect is on diagnostics, not behavior.
FromKeyedServicesAttribute no longer injects non-keyed parameterWhen
[FromKeyedServices]names a keyed service that isn’t registered, anInvalidOperationExceptionis thrown instead of possibly injecting a wrong, non-keyed service. Also backported to .NET 8.0.9.- How to tell
[FromKeyedServices(key)]parameters;InvalidOperationExceptionat resolution time.- What to do
- Register the service as keyed (
AddKeyedScoped/AddKeyedSingleton/AddKeyedTransient). The .NET 8.0.9 opt-out switch doesn’t exist in .NET 9. - Why it’s rare (our view)
- Needs keyed DI with a missing registration; apps on patched .NET 8 (8.0.9+) already have this behavior.
CET supported by defaultapphost and singlefilehost are marked Intel CET-compatible. Loaded native libraries that set thread context to locations not on the shadow stack cause process termination.
- How to tell
- Process terminated when a native library that manipulates thread context is loaded (Windows hardware-enforced stack protection).
- What to do
- Opt out with
<CETCompat>false</CETCompat>in the project file, or exclude the app via Windows Security exploit protection/group policy. - Why it’s rare (our view)
- Only Windows hosts using an apphost executable with unusual native libraries are affected.
Windows private key lifetime simplifiedOn Windows, when a PFX loads without
PersistKeySetorEphemeralKeySet, private-key lifetime is now always tied to theX509Certificate2instance produced directly from the load.- How to tell
- Windows code that imports PFX files via
X509Certificate2Collection.Importand relied on disposing a clone to erase the key. - What to do
- If you relied on disposing a clone to erase the key, also (or instead) dispose the original loaded certificate object.
- Why it’s rare (our view)
- Only Windows/IIS apps with specific PFX collection-loading and disposal patterns are affected.
.NET runtime no longer provides default termination signal handlersThe runtime no longer installs default SIGTERM (Unix) or
CTRL_SHUTDOWN/CTRL_CLOSE(Windows) handlers, so without other handlers the process exits immediately andProcessExitisn’t raised.- How to tell
- Code relying on
AppDomain.ProcessExitorAssemblyLoadContext.Unloadingduring container/service shutdown in apps that don’t use the generic host. - What to do
- Typical ASP.NET apps and hosts using
UseConsoleLifetimeneed nothing. Otherwise register handlers withPosixSignalRegistration.Create. - Why it’s rare (our view)
- ASP.NET Core and Generic Host workers register their own signal handlers; only hand-rolled console workers are affected.
LDAP DirectoryControl parsing is now more stringent
DirectoryControlparsing inSystem.DirectoryServices.Protocolsmoved to managed code and is stricter (ASN.1 tags, lengths, no trailing data, invalid UTF-8 throws), consistent across platforms.- How to tell
LdapConnection.SendRequest/EndSendRequestexceptions against non-compliant LDAP servers;EncoderFallbackExceptionfrom theVlvRequestControlconstructor.- What to do
- Make sure the LDAP server complies with the RFCs; handle
EncoderFallbackExceptionwhen calling theVlvRequestControlconstructor. - Why it’s rare (our view)
- Only apps using
System.DirectoryServices.Protocolsagainst non-compliant servers are affected.
Named mutexes default to user scope on UnixNamed mutexes on Unix now default to per-user scope and storage (servicing change in 8.0.32, 9.0.21, 10.0.13). Creation fails for UIDs without a user-database entry.
- How to tell
- Named
Mutexconstructors on Linux; cross-user mutex sharing stops working; failures in containers run withdocker run -u <uid>or KubernetesrunAsUser. - What to do
- For cross-user access pass
NamedWaitHandleOptions.Noneor setSystem.Threading.Mutex.UnsafeDefaultMutexScopeGlobal; in containers setXDG_RUNTIME_DIRor add a user. - Why it’s rare (our view)
- Named mutexes are uncommon in web apps, but the arbitrary-UID container failure mode is worth noting.
.NET 10 requires OpenSSL 1.1.1 or later on UnixOn Unix platforms where .NET uses
OpenSSL(such as Linux),OpenSSL1.1.1 or later is required; otherwise the app fails to start.macOSisn’t affected.- How to tell
- App fails to start on an old Linux distribution or custom base image that has only
OpenSSLolder than 1.1.1. - What to do
- Use a Linux or Unix distribution that includes
OpenSSL1.1.1 or later. - Why it’s rare (our view)
- Current distros and official images ship
OpenSSL1.1.1 or 3.x; only very old custom base images are affected.
Environment variable renamed to DOTNET_OPENSSL_VERSION_OVERRIDEThe Linux setting
CLR_OPENSSL_VERSION_OVERRIDE, which picks the preferredOpenSSLlibrary version, is renamed toDOTNET_OPENSSL_VERSION_OVERRIDE.- How to tell
CLR_OPENSSL_VERSION_OVERRIDEset in Dockerfiles, Kubernetes manifests or host environment.- What to do
- Use
DOTNET_OPENSSL_VERSION_OVERRIDEinstead ofCLR_OPENSSL_VERSION_OVERRIDE. - Why it’s rare (our view)
- Few deployments pin the
OpenSSLversion through this variable.
Environment variable renamed to DOTNET_ICU_VERSION_OVERRIDEThe Linux setting
CLR_ICU_VERSION_OVERRIDE, which picks the preferred ICU library version, is renamed toDOTNET_ICU_VERSION_OVERRIDE.- How to tell
CLR_ICU_VERSION_OVERRIDEset in Dockerfiles, Kubernetes manifests or host environment.- What to do
- Use
DOTNET_ICU_VERSION_OVERRIDEinstead ofCLR_ICU_VERSION_OVERRIDE. - Why it’s rare (our view)
- Few deployments pin the ICU version through this variable.
Single-file apps no longer look for native libraries in executable directorySingle-file apps no longer add the executable’s directory to native library search paths, and
NativeAOTno longer sets rpath to the app directory by default.- How to tell
DllNotFoundExceptionfor P/Invoke orNativeLibrary.Loadin apps published withPublishSingleFileorNativeAOTthat ship native libraries next to the executable.- What to do
- Specify
DllImportSearchPath.AssemblyDirectorywhere needed; forNativeAOTadd the rpath linker arguments explicitly. - Why it’s rare (our view)
- Only single-file or
NativeAOTdeployments that load native libraries from the app folder.
Uri length limits removedUri constructors and
TryCreateno longer cap URI strings at about 65,000 characters; paths, queries and fragments can be practically unlimited.- How to tell
- Input validation that relied on
new Uri(…)orUri.TryCreatefailing for very long strings. - What to do
- If you relied on Uri for length validation, check lengths yourself before constructing the Uri.
- Why it’s rare (our view)
- Few apps rely on Uri’s length cap for validation, but those that did lose a safeguard silently.
NuGet packages with no runtime assets aren't included in deps.jsonPackages or libraries contributing no runtime assets may be dropped from
deps.jsonif removing them doesn’t break dependency paths.- How to tell
- Tools or security scanners that read the app’s
deps.jsonand expect every referenced package to be listed. - What to do
- To keep the old behavior, set the MSBuild property
TrimDepsJsonLibrariesWithoutAssetsto false. - Why it’s rare (our view)
- The runtime is unaffected; only tooling that inspects
deps.jsonnotices.
ASP.NET Core · 4
DefaultKeyResolution.ShouldGenerateNewKey altered meaningData Protection’s
DefaultKeyResolution.ShouldGenerateNewKeyno longer signals that the default key is near expiry; it now means no default key or a policy reason.- How to tell
- Custom
IDefaultKeyResolverimplementations or code readingDefaultKeyResolution.ShouldGenerateNewKey. - What to do
- Remove replicated expiry logic from custom resolvers (optional); check the default key’s
ExpirationDatedirectly instead. - Why it’s rare (our view)
- Only custom Data Protection key resolvers or code consuming this internal-namespace API.
Dev cert export no longer creates folder
dotnet dev-certs https exportno longer creates a missing target directory; the export fails instead.- How to tell
There was an error exporting the HTTPS developer certificate to a file.when runningdotnet dev-certs https -epto a non-existent folder (for example in dev scripts).- What to do
- Create the target directory, with appropriate permissions, before running
dotnet dev-certs. - Why it’s rare (our view)
- Only dev/CI scripts that export the dev cert into a folder that doesn’t exist yet; first appeared in .NET 8.0.10 too.
IncludeOpenAPIAnalyzers property and MVC API analyzers are deprecatedSetting
IncludeOpenAPIAnalyzersto true now emits warningASPDEPR007; the MVC API analyzers still work but will be removed in a future release.- How to tell
- Warning
ASPDEPR007from projects with<IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>. - What to do
- Remove the
IncludeOpenAPIAnalyzersproperty and migrate toTypedResults; or suppressASPDEPR007viaNoWarntemporarily. - Why it’s rare (our view)
- Opt-in MSBuild property that few projects set.
Microsoft.Extensions.ApiDescription.Client package deprecatedThe
Microsoft.Extensions.ApiDescription.Clientpackage is deprecated; referencing projects get build warnings and itsOpenApiReferenceMSBuild targets anddotnet openapiCLI are no longer supported.- How to tell
PackageReferencetoMicrosoft.Extensions.ApiDescription.Client,<OpenApiReference>items,OpenApiProjectReference, ordotnet openapicommands.- What to do
- Remove the package; generate clients with NSwag, Kiota or OpenAPI Generator tooling, committing output or using a custom pre-build step.
- Why it’s rare (our view)
- Only apps generating OpenAPI clients at build time via
OpenApiReference.
EF Core · 12
Microsoft.EntityFrameworkCore.Design not found when using EF toolsWith .NET SDK 9.0.200+, EF tools can fail to load
Microsoft.EntityFrameworkCore.Designwhen it is referenced as a private asset; Microsoft says EF Core 10 fixes it.- How to tell
- ‘Could not load file or assembly
Microsoft.EntityFrameworkCore.Design...’ when runningdotnet efor PMC tools. - What to do
- Until you’re on EF Core 10, add
<Publish>true</Publish>to the DesignPackageReference(the DLL is then copied to the output and publish folders). - Why it’s rare (our view)
- Microsoft rates it Medium but says EF Core 10 fixes it, so a direct move to EF Core 10 shouldn’t hit it. It matters only if you stop on EF Core 9 with SDK 9.0.200 or later.
ToString() method now returns empty string for null instancesIn queries,
ToString()on a null value now consistently returns an empty string; previously results varied (null, “True”, or empty) by type and context.- How to tell
- LINQ queries calling
ToString()on nullable properties (for example bool?) whose results now come back as empty string instead of null. - What to do
- Rewrite the query with an explicit null check, for example
x.NullableBool == null ? null : x.NullableBool.ToString(). - Why it’s rare (our view)
- Microsoft rates it Low; only queries projecting
ToString()of nullable values.
SQL Server IndexOf now propagates null valuesOn SQL Server,
IndexOfon a null string now returns null rather than 0 (for empty search strings), matching SQLite.- How to tell
- SQL Server queries using
string.IndexOfon nullable string columns. - What to do
- Handle null explicitly in the query, for example
e.NullableString == null ? 0 : e.NullableString.IndexOf(...). - Why it’s rare (our view)
- Microsoft rates it Low; narrow query pattern.
Compiled models now reference value converter methods directlyCompiled models (
dotnet ef dbcontext optimize) now generate code calling value converter methods directly, so private conversion methods cause compilation failures.- How to tell
- Build errors in generated compiled-model code after
dotnet ef dbcontext optimize, when aValueConverteruses private static methods. - What to do
- Make methods referenced by value converters public or internal.
- Why it’s rare (our view)
- Microsoft rates it Low; only apps using compiled models with custom converters.
EF.Constant() and EF.Parameter() no longer work inside compiled queriesUsing
EF.ConstantorEF.ParameterinsideEF.CompileQuery/CompileAsyncQuerynow throws anInvalidCastException.- How to tell
InvalidCastException‘Unable to cast object of typeSystem.Linq.Expressions.ConstantExpressionto typeSystem.Linq.Expressions.ParameterExpression’.- What to do
- Remove
EF.Constant/EF.Parameterfrom the compiled query, or stop compiling that query. - Why it’s rare (our view)
- Microsoft rates it Low; needs both compiled queries and
EF.Constant/EF.Parameter.
Some NoTrackingWithIdentityResolution queries are now prohibited for JSON collectionsEF Core 9 throws for some
AsNoTrackingWithIdentityResolutionqueries over JSON-mapped collections, including LINQ operators applied directly to JSON collection navigations.- How to tell
- Exceptions from
AsNoTrackingWithIdentityResolution(orNoTrackingWithIdentityResolution) queries that useOrderBy,Where,SkiporTakeon JSON collection navigations. - What to do
- Use a tracking query if identity resolution is needed; otherwise use
AsNoTracking. - Why it’s rare (our view)
- Microsoft rates it Low; needs JSON columns plus identity-resolution no-tracking queries.
EF.Functions.Unhex() now returns byte[]?
EF.Functions.Unhex()(SQLite) is now annotated as returningbyte[]?because SQLite returns NULL for invalid input.- How to tell
- New nullable-reference warnings where
EF.Functions.Unhex()results are used. - What to do
- Add the null-forgiving operator if inputs are always valid hex; otherwise add null checks.
- Why it’s rare (our view)
- Microsoft rates it Low; SQLite-only function, compile-time nullability warning.
Application Name is now injected into the connection stringEF now adds an Application Name to SQL Server connection strings lacking one, so mixed EF and ADO.NET/Dapper access in
TransactionScopemay escalate to distributed transactions.- How to tell
- Unexpected distributed-transaction escalation, or separate connection pools, when EF and Dapper/ADO.NET share a database inside a
TransactionScope. - What to do
- Define an Application Name in your connection string; EF then leaves it unchanged.
- Why it’s rare (our view)
- Microsoft rates it Low; edge case needing mixed data access inside
TransactionScope.
ExecuteUpdateAsync now accepts a regular, non-expression lambda
ExecuteUpdate/ExecuteUpdateAsynccolumn setters are now a Func rather than an Expression, so hand-built setter expression trees no longer compile.- How to tell
- Compile errors where code builds
Expression<Func<SetPropertyCalls<T>, SetPropertyCalls<T>>>forExecuteUpdate. - What to do
- Rewrite as a statement lambda that calls
SetPropertyconditionally. - Why it’s rare (our view)
- Microsoft rates it Low; only code that builds setter expression trees dynamically.
Complex type column names are now uniquifiedWhen properties in different complex types map to the same column name, EF now appends a number instead of silently sharing one column.
- How to tell
- Unexpected migrations renaming/adding columns for complex types with clashing column names.
- What to do
- To share a column intentionally, configure each property explicitly with
HasColumnName. - Why it’s rare (our view)
- Microsoft rates it Low; complex types (EF 8+) with colliding column names.
Nested complex type properties use full path in column namesProperties of nested complex types now map to columns named with the full path (
Complex_NestedComplex_Property) instead ofNestedComplex_Property.- How to tell
- Migrations renaming columns for nested complex type properties after upgrading.
- What to do
- Keep old names by configuring them explicitly with
HasColumnName. - Why it’s rare (our view)
- Microsoft rates it Low; only models with nested complex types.
Writing DateTimeOffset into REAL column now writes in UTC
Microsoft.Data.Sqlite10 convertsDateTimeOffsetvalues to UTC before writing them to REAL columns; previously the offset was ignored.- How to tell
- SQLite apps storing
DateTimeOffsetin REAL columns; newly written values differ from older rows. - What to do
- Adjust code; as a temporary last resort set the
Microsoft.Data.Sqlite.Pre10TimeZoneHandlingAppContextswitch to true. - Why it’s rare (our view)
- Microsoft rates it High, but needs SQLite plus
DateTimeOffsetstored in REAL columns.
What this page leaves out
124 of the 194 entries on Microsoft’s lists aren’t here. Each list as it stood on 25 October 2026, with the day Microsoft last updated it:
| List | Listed | Here | Out of scope | Updated |
|---|---|---|---|---|
| .NET 9 | 61 | 14 | 10 for Windows Forms and WPF | |
| .NET 10 | 80 | 21 | 8 for Windows Forms and WPF | |
| ASP.NET Core 9 | 6 | 5 | 1 for Blazor WebAssembly only | |
| ASP.NET Core 10 | 9 | 9 | None | |
| EF Core 9 | 24 | 9 | 10 for the Azure Cosmos DB provider | |
| EF Core 10 | 14 | 12 | None |
Out of scope
Windows Forms, WPF and Blazor WebAssembly changes, and the Azure Cosmos DB provider’s, are left out: this page is about server apps.
Judged not to apply
The rest are APIs and tools a typical web app or worker doesn’t use, such as changes for library and tooling authors, or for interop and reflection edge cases. If your app does use one, its list has it.
Listed twice
A change Microsoft lists twice on one list counts once in “Listed”. A few changes appear on both the .NET 9 and .NET 10 lists and count on each; any we include is here once.
Sources
Microsoft’s six lists, each read in full on the day shown. Every entry above links to its own change page, checked the same day.
- Breaking changes in .NET 9Microsoft Learn, checked
- Breaking changes in .NET 10Microsoft Learn, checked
- Breaking changes in ASP.NET Core 9Microsoft Learn, checked
- Breaking changes in ASP.NET Core 10Microsoft Learn, checked
- Breaking changes in EF Core 9Microsoft Learn, checked
- Breaking changes in EF Core 10Microsoft Learn, checked
About this page
Cite this page
SnowDriven. “.NET 8 to .NET 10: the breaking changes that hit web apps.” SnowDriven Research. https://snowdriven.com/research/net-10-breaking-changes/ (last updated 25 October 2026).
One click or tap selects the whole citation, ready to copy.
About these facts
Each entry summarizes Microsoft’s own page for that change, linked and checked on the day shown; the likely, possible and rare ratings, and the reasons for them, are ours. Microsoft can change its lists and pages without notice, and our summary can fall behind or contain mistakes, so read the linked page before you change a system. Information only, provided as is (terms). Reuse: free to quote, with credit to SnowDriven and a link (terms of use).
Start a project
Want this list checked against your code?
We can go through your solution with it: which entries apply, what each takes to fix, and the order to move services in. Tell us what you run and where it’s hosted; we reply within one business day.
Pittsburgh, PA · Since 2015 · First step: a 30-minute call