Build1 distinct publisher3 min readUpdated
The rendered message still sits at the top level, but code that walked into State to find it now gets null with no error. Microsoft calls the change a behavioral breaking change.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
The failure is quiet because JSON extraction usually is. An extractor that walks to `State` and then to `Message` returns null when the nested key is absent rather than raising anything [2], and null is a plausible value for a log line. The record still arrives with its level and timestamp and lands in the index with an empty body. Nothing downstream can tell the difference between an event that had no text and a parser that stopped looking in the right place.
The delta itself is one deleted duplicate. In the before-and-after pair published in the dev.to writeup, `State` goes from four properties to three, and the rendered sentence appears once in the record instead of twice [3][4][1]. Everything a query would actually filter on stays where it was: `OrderId`, `Status`, and the `{OriginalFormat}` template [7]. Searching a rendered sentence for an order ID was always throwing away the reason to emit JSON in the first place [15].
Read order matters more than it looks. The writeup's rule is top-level first, nested as fallback, and the fallback exists for stored .NET 9-era records or mixed-version fleets rather than as a permanent anchor [8]. That ordering is not decoration: Microsoft's compatibility note says `State.Message` can still appear when its content differs from the top-level value [6], and in that case the top-level field is canonical [9]. A parser that checks the nested location first will therefore pick the wrong string in precisely the records where the two strings disagree.
The transferable part is the test, not the field name. The author treats console JSON as a schema whenever another process parses it, which makes a runtime upgrade a contract change rather than something to eyeball in a terminal [10]. What makes his sample useful is where the current side of the contract comes from: an emitter configured with `AddJsonConsole` runs under the installed runtime, and a verifier captures its single line and checks semantics rather than property order or timestamp text [11][12]. Two hand-authored fixtures would have sailed through this upgrade agreeing with each other and with nothing else. The test is deterministic and offline after restore, with no logging backend, account, or credential involved [13].
Scope is worth keeping narrow. The sample was verified on SDK 10.0.303 and runtime 10.0.11, though the contract change belongs to .NET 10 generally rather than that patch [14]. Other `AddJsonConsole` options for timestamps, scopes, and JSON handling can reshape other parts of a record, which is why the contract covers only the fields the consuming pipeline reads [16]. And if the pipeline goes through a provider or an OpenTelemetry exporter, that schema is better defined and should be tested directly instead of being translated into the console formatter's shape [17]. Anyone parsing the built-in formatter has a dependency on an implementation detail that ships breaking changes on the major-version cadence, and the cheapest defense available is a test that runs the formatter and reads what comes out.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
.NET 10's JSON console logging still emits the formatted message, but a typical record no longer duplicates it at State.Message.
A collector, script, or snapshot test that reads only the nested State.Message property can start returning null while the application continues logging normally.
Before .NET 10, a normal AddJsonConsole record commonly repeated the rendered text, with the same sentence at top-level Message and at State.Message alongside OrderId, Status and {OriginalFormat}.
In .NET 10 the typical record keeps one rendered message at the top level, with State holding OrderId, Status and {OriginalFormat}.
Microsoft documents the change as a behavioral breaking change and recommends that parsers use the top-level property.
The official compatibility note says State.Message may still appear when its content differs from the top-level value, so a record containing both properties should not be rejected.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Demonstrated in code, but single-source and secondhand on the official documentation
The core technical assertion is shown rather than asserted: paired before/after records, a brittle legacy extractor, a top-level-first replacement, and a verifier that exercises the installed runtime instead of a hand-authored fixture. That is unusually concrete for a single post. It is still one publisher with no independent reproduction, and the load-bearing external facts — Microsoft's breaking-change classification, the compatibility caveat, and the 10.0.11 release-note reference — are relayed in prose with no supplied primary document in the cluster.
Change is shipping in released .NET 10; downstream impact unmeasured
There is one hard adoption fact: the new record shape is present in released .NET 10 bits, verified by the author against SDK 10.0.303 and runtime 10.0.11, plus a published runnable sample encoding the contract test. What the cluster does not contain is any measure of exposure — no count of collectors, agents, snapshot tests, or vendor pipelines that key on State.Message, and no report of an actual outage or broken ingest. Adoption is therefore scored on shipped-runtime availability only, not on evidence of practitioner uptake of the recommended pattern.
Slightly understated relative to its operational reach
The framing is calibrated and self-limiting: the author declines to claim State.Message can never appear, keeps the fallback explicitly narrow, notes the change is not a loss of structured data, and bounds the technique to consumers of the built-in formatter while pointing to provider and OpenTelemetry schemas as better contracts. The dek's 'parsers go quiet' is dramatic but accurate to the described null-return behavior. If anything the post undersells the breadth of the silent-failure class, since it offers no exposure estimate for the pipelines it warns about.
Practitioner authorship promoting an approach and sample, no commercial stake evidenced
The visible incentive is authorial: a developer-blog post advancing a personal framing ('console JSON is a schema'), a named extraction rule, and an accompanying runnable sample, closing with an engagement prompt to readers. That biases toward presenting the pattern as necessary practice. There is no evidence of vendor sponsorship, paid product, affiliate offer, or commercial dependency — the sample is explicitly described as needing no account, credential, or paid service — and the post recommends non-authored alternatives such as provider and OpenTelemetry pipelines.
Technically credible and internally consistent, limited by one publisher and unquantified exposure
Confidence is moderate. The mechanism is specific, reproducible in principle, version-pinned, and internally consistent, and the caveats align with how a documented behavioral change would be expected to behave. It is nonetheless a single-publisher cluster whose official-documentation references cannot be checked from the supplied material, and the practical stakes — how much tooling actually reads State.Message — are entirely unmeasured.
build
MTP 2.3 writes TRX as tests finish, so a dead test host no longer erases the evidence1 distinct publisher
build
.NET 10 fails restore on a versionless PackageReference, and the reflexive fix breaks CPM1 distinct publisher
product
Rillet's $100M reads as proof mid-market ERP is rip-and-replace, mostly at the cheap end1 distinct publisher
build
A UDP packet is now enough: IKEEXT RCE moves from patch queue to fire drill1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 22, 2026