Build1 distinct publisher3 min readPublished
On .NET 9 a lone UTF-16 surrogate in a virtual list view target became EF BF BD and reached the server as a target the application never chose. On .NET 10 the encoder refuses, and the failure lands at the client.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Look at what the old fallback did on the wire. `GetValue()` serializes the control, and the target string has to go from UTF-16 to UTF-8 on the way [5]. A lone high surrogate such as `\uD800` has no scalar value to encode [6], so the encoder substitutes U+FFFD and emits its three UTF-8 bytes, EF BF BD [7]. The result is well formed BER, the server accepts it, and it returns a window around whatever position those bytes sort to in the result set [4][7]. Nothing in that exchange reports a problem, and the client that asked for one page gets another [7].
A surrogate pair is two UTF-16 code units standing for one scalar value [6]; cut a display name or a sort key at a fixed number of code units and you can land between the halves. That is the shape of bug the .NET 10 exception catches, and it catches it during serialization, before LDAP I/O [2]. So a `try`/`catch` wrapped around the request itself will not contain it. The author of the dev.to walkthrough treats the exception as input validation surfacing at the correct boundary rather than as an LDAP server failure [16], and his remedy is to trace the value back to its source, validate there, and keep the boundary test so a later refactor cannot restore the permissive path by accident [14].
The probe is worth copying for its shape. Four inputs: ASCII, a valid surrogate pair, an unpaired high surrogate, an unpaired low surrogate; `GetValue()` called directly so BER serialization runs with no connection open; a hex scan of the serialized bytes for EFBFBD [10]. Two of those four inputs behave identically on both runtimes, which makes the other two the whole compatibility surface [15]. `Summary: 5/5 passed` [12] is a claim about the author's harness. For the split to reproduce in yours, the VLV target has to arrive as a .NET string from a path that can emit a lone surrogate [5][6], and you have to be on the pairing he pinned: net10.0 with System.DirectoryServices.Protocols 10.0.11 [9].
The experiment also moves two variables together. Package versions are pinned per target framework, 9.0.19 for net9.0 and 10.0.11 for net10.0 [9], so it demonstrates the .NET 9 to .NET 10 delta as shipped without isolating whether the behaviour changed in the runtime or in the library. The counting is loose in one place too: the post describes six assertions per target and a verifier that ends at 5/5 [11][12], and those are not the same axis. Neither point weakens the finding, and the harness has the property that decides whether anyone adopts it, which is that it needs no directory, no credentials and no runtime network access [12]. A compatibility test with no server to provision is one that might actually get run. Microsoft documents the strict encoding behaviour in a .NET 10 LDAP compatibility note, according to the same post [8]. In my context I would take the throw and pay the migration cost: a paging control that seeks to an unintended position is more expensive to diagnose than an exception at the call site [3][7].
Ranked by verification strength, evidence, and original report placement.
On .NET 9, an unpaired UTF-16 surrogate in a virtual-list-view target can be encoded as the UTF-8 replacement sequence EF BF BD.
The older fallback behaviour substitutes U+FFFD, whose UTF-8 bytes are EF BF BD; that keeps serialization moving but silently changes the VLV target and may seek to an unintended position.
On .NET 10, the same malformed virtual-list-view target is rejected with EncoderFallbackException before LDAP I/O.
The stricter .NET 10 behaviour is safer because a client no longer sends bytes that differ from the string it was given, and it is still a breaking change if an application has been allowing malformed strings to reach VlvRequestControl.
LDAP virtual list view controls let a client request a window around a target in a sorted result set.
The VlvRequestControl API accepts the target as a .NET string, which must be converted from UTF-16 to UTF-8 when the control value is serialized.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 1, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
Console JSON is a schema: .NET 10 dropped the duplicate State.Message, and parsers go quiet1 distinct publisher
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
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.
Reproducible, but self-certified
The strongest thing about this reporting is that it hands you the falsifier: full csproj, full program, exact package versions, an expected result matrix stated in advance. The weakest is that one person on one machine is the entire evidence base, and the vendor note credited with causing the whole change is mentioned rather than cited. A reader can verify the behaviour in ten minutes — which is precisely why nobody in our coverage has.
Two package pins and one laptop
Adoption evidence here begins and ends with a csproj: stable System.DirectoryServices.Protocols packages exist for both runtimes, and an offline verifier passed on the author's machine. No migration is described tripping over the new exception, no directory operator reports a mis-seek from the old replacement bytes, no downstream project is shown adopting the boundary test. The change has shipped; whether anyone has met it in anger is unknown.
Quieter than its own headline
The title issues an order — stop sending replacement bytes — and then the body spends its last third arguing against its own importance: if you never construct VlvRequestControl this test buys you nothing, a broader Unicode policy may be the better investment, and none of it proves your directory supports virtual list view at all. Very few posts about a UTF-16 edge case volunteer that much scope reduction. The substance sits slightly under the volume, not above it.
Reputation, not revenue
A developer-platform post that links its author's repository and merged pull request is paid in credibility, and credibility here is earned the expensive way — by publishing code a reader can run against you. There is no product to sell, no sponsor, no licence being defended. The one soft spot is the appeal to Microsoft's compatibility note, which lends the piece authority the reader cannot check.
Believable, unconfirmed
The mechanism is mundane enough to be believed on sight — an encoder that used to substitute U+FFFD now throws — and the code offered to prove it is complete. What keeps this in the middle band is arithmetic: one publisher, one author, zero independent runs, and an internal count that does not tie out. Confidence would move sharply on a single reader reproducing the 5/5 or on the vendor note being put on the page.