Skip to content

Why an AI-Generated Table Sorts Accented Names Incorrectly

9 min read · updated August 11, 2026

Álvarez appears after Zetterberg. Öberg is at the very bottom of the list. The sort ran, produced a stable order, and threw no error; it simply sorted by a number that has nothing to do with alphabetical order in any language.

Everything accented is at the bottom

The signature of the bug is that the list looks correct until the end, where a small cluster of accented names sits after Z in what appears to be an arbitrary order. It is not arbitrary: it is the order of the Unicode code points, and it is completely deterministic.

Generated code produces this by default in every language, because the default comparison operator on strings is a code-unit comparison — Array.prototype.sort() with no comparator in JavaScript, sorted() in Python, String.compareTo in Java, and ORDER BY on a column with a byte-ordered collation in SQL. None of these is broken. They are answering a different question from the one the interface is asking.

What a default sort actually compares

The unaccented Latin capitals occupy U+0041 to U+005A. Accented Latin letters live in the Latin-1 Supplement and later blocks, from U+00C0 upwards, and letters with carons and other marks live higher still:

A  U+0041      Z  U+005A
Á  U+00C1      Ä  U+00C4      Å  U+00C5
Ö  U+00D6      Ø  U+00D8      Š  U+0160

So every accented capital sorts after every unaccented one, which is why the cluster forms at the end. Note also that Å (U+00C5) is greater than Ä (U+00C4) in code-point order, which is the opposite of the Swedish alphabet’s order — so even the internal order of the bottom cluster is wrong for the language it comes from.

One list, three orders

Take this list of eight surnames and sort it three ways:

input     Andersson, Álvarez, Ärlig, Åberg, Öberg, Østergaard,
          Zetterberg, Šimek

code point   Andersson
             Zetterberg          <- Z (U+005A) before every accent
             Álvarez             (U+00C1)
             Ärlig               (U+00C4)
             Åberg               (U+00C5)
             Öberg               (U+00D6)
             Østergaard          (U+00D8)
             Šimek               (U+0160)

Swedish      Álvarez             á has the primary weight of a
             Andersson
             Šimek               š has the primary weight of s
             Zetterberg
             Åberg               Å, Ä, Ö are letters 27, 28, 29
             Ärlig                 of the alphabet, after Z
             Öberg
             Østergaard          ø sorts with ö in Swedish

German       Åberg               dictionary order (DIN 5007-1):
             Álvarez               ä, ö, ü sort as a, o, u
             Andersson
             Ärlig
             Öberg
             Østergaard
             Šimek
             Zetterberg

Three completely different answers, and only the first is wrong in the sense of being wrong for everybody. The Swedish and German orders are both correct — for Swedish and German readers respectively. Åberg is second from last in Swedish and first in German, from the same input.

There is no single correct order

That is the fact to design around, and it is uncomfortable because it means the question “is this list sorted correctly?” has no answer until you say for whom.

  • Swedish, Danish, Norwegian and Finnish treat the accented vowels as distinct letters that follow Z. Danish and Norwegian order them Æ, Ø, Å; Swedish orders them Å, Ä, Ö.
  • German has two standard orders. DIN 5007-1, dictionary order, treats ä as a. DIN 5007-2, phonebook order, treats ä as ae, which changes where names like Müller and Mueller sit relative to each other. Both are standard; they are used for different purposes.
  • Spanish places ñ as a separate letter directly after n, so Peña follows Pena and precedes Pera. Ch and ll were also separate letters until the Real Academia Española’s 1994 decision, so old indexes are ordered differently from new ones.
  • Estonian is the case that shows this is not just about accents: Z sits between S and T in the Estonian alphabet, so an Estonian list is not merely the English one with the accents moved.
  • Czech and Slovak treat č, ř, š and ž as distinct letters following their base letters, so Šimek does not sort with S there as it does in Swedish.

All of this is captured in the Unicode Collation Algorithm, UTS #10, and its per-locale tailorings in CLDR. The algorithm’s structure is what makes the German and Swedish results both expressible: it compares at several levels, where the primary level is base letters and later levels handle accents and case, and a locale tailoring changes which level a given mark contributes at. In German, the umlaut is a secondary difference; in Swedish, it is a primary one.

Normalization makes it worse

Before a sort can be right, the input has to be in a consistent Unicode form. Ö can be one code point, U+00D6, or two — O followed by U+0308 COMBINING DIAERESIS — and both display identically.

Under a code-point sort those two forms land in completely different places: the decomposed one sorts adjacent to plain O, in the middle of the list, while the precomposed one goes to the bottom. So a list can contain the same name twice, in two positions, looking identical. This is the same underlying issue as the one that makes a length check on a name field return different verdicts for the same name, and the same fix applies: normalise to NFC on the way in.

Collation data is versioned, and changing it reorders indexes. The glibc 2.28 release changed many locale collation definitions, which invalidated existing PostgreSQL indexes built under the old rules on affected systems — a real, widely encountered operational hazard rather than a theoretical one. If your database sorts by a system collation, an OS upgrade is a data-ordering event.

Fixing it at each layer

  • JavaScript. Pass a comparator built from Intl.Collator, never the bare sort(): list.sort(new Intl.Collator(locale).compare). Add sensitivity and numeric options where you need case-insensitive or natural-number ordering.
  • PostgreSQL. Use an ICU collation on the column or in the query rather than the database default, which is often the C locale or a system locale that will move under you. ORDER BY surname COLLATE "sv-SE-x-icu" is explicit and stable across hosts in a way the default is not.
  • Store one form, sort by another. The stored name is the user’s; the sort key is yours. Keeping a normalised, locale-collated sort key alongside the display name makes the ordering reproducible and lets you change the collation without touching the source data.
  • Decide whose alphabet the list is in. For a single-market product, the market’s locale. For a multilingual one, the viewer’s locale — which means the same list is legitimately ordered differently for two users, and any UI that references a position (“the third row”) or paginates by an alphabetical range has to account for it.
  • Do not ask a model to sort the list. It will produce a plausible order, it will not be reproducible between calls, and for a list longer than a screen it will quietly drop or duplicate rows. Sorting is a solved deterministic problem with a correct implementation on every platform.

The Polish case has its own set of tailorings and is worth reading separately in Polish diacritics and sort order, as is the storage-side question in normalising accented names in a database.