SORT means two different things: Excel's sort_order vs Google Sheets' is_ascending
Two spreadsheet vendors gave the third argument of SORT two different jobs, and the
argument accepts the other one’s value without complaining. Excel’s third argument is a
signed sort_order: 1 ascending, -1 descending. Google
Sheets’ third argument is a boolean is_ascending: TRUE ascending,
FALSE descending. Hand Sheets the Excel value -1 and you do not get an
error — you get the list sorted the wrong way up.
We executed it. Our how-to corpus’s =SORT(A2:A4,1,-1) over
A2:A4 = 20, 50, 10 returned 50, 20, 10 in LibreOffice Calc 25.8.7.3 and
10, 20, 50 in the Google Sheets run of 2026-08-30. Same formula, opposite order, no
error message on either side.
The surprise
This breaks in exactly one direction, which is what makes it hard to spot. A formula that
sorts ascending travels fine: Excel writes 1 for ascending, and
1 is also truthy, so Sheets reads it as is_ascending = TRUE and agrees
by accident. Our =SORT(A1:B3,2,1) case matched in both engines for exactly that
reason. Only the descending formulas invert — because -1, the Excel
value that means “descending”, is a nonzero number and therefore reads as
“true”, which in Sheets means ascending. Half your sorts survive the trip and half
of them silently reverse, and nothing anywhere says so.
What each vendor documents the third argument to be
Neither engine is misbehaving. Both are doing precisely what their own help page says, and the help pages disagree. We fetched both on 2026-08-30.
Google Sheets. The
SORT function help article gives the
signature as SORT(range, sort_column, is_ascending, [sort_column2, is_ascending2, ...])
and describes the third argument as “TRUE or FALSE indicating
whether to sort sort_column in ascending order. FALSE sorts in descending
order.” Its sample usage is SORT(A2:B26, 1, TRUE). There is no mention of
1 or -1 anywhere on the page, and no note about what happens if you pass
a number.
Microsoft Excel. The
SORT
function page gives the signature as =SORT(array,[sort_index],[sort_order],[by_col])
and describes the third argument as “A number indicating the desired sort order; 1 for
ascending order (default), -1 for descending order.” The fourth argument,
[by_col], is the boolean one there: “A logical value indicating the desired sort
direction; FALSE to sort by row (default), TRUE to sort by column.”
So the two signatures put a number and a boolean in the same slot, and Excel’s boolean sits
one position further right, where Sheets expects sort_column2. We have executed cases
for the three-argument form only; the four-argument collision is what the two documented signatures
imply, not something we measured, and we are not claiming a result for it.
Executed: the SORT function corpus across four LibreOffice builds and Google Sheets
These are the four SORT cases in our function corpus, run as written. Inputs:
the first two cases read A1:A5 = 3, 1, 4, 1, 5; the third reads
A1:B3 = x/30, y/10, z/20; the fourth has no setup cells at all, so A1 is
blank. The Excel column is Microsoft’s documented behaviour and is the value every case is
measured against — we do not run Excel. Because these formulas spill, each executed cell
shows the anchor value followed by the read-back of the whole recorded check range.
| Case | Formula | Excel (documented) | Google Sheets (Drive import, 2026-08-29) | LibreOffice 24.2.0.3 (executed) | LibreOffice 24.8.7.2, 25.2.0.3 & 25.8.7.3 (executed) |
|---|---|---|---|---|---|
| SORT_default_ascending | =SORT(A1:A5) | 1, 1, 3, 4, 5 | 1 — read-back 1, 1, 3, 4, 5 (matches) | #NAME? | 1 — read-back 1, 1, 3, 4, 5 (matches) |
| SORT_descending | =SORT(A1:A5,1,-1) | 5, 4, 3, 1, 1 | 1 — read-back 1, 1, 3, 4, 5 (ascending; does not match) | #NAME? | 5 — read-back 5, 4, 3, 1, 1 (matches) |
| SORT_by_second_column | =SORT(A1:B3,2,1) | y, 10 / z, 20 / x, 30 | y — read-back y, 10, z, 20, x, 30 (matches) | #NAME? | y — read-back y, 10, z, 20, x, 30 (matches) |
| SORT_empty_range_error | =SORT(A1:A1) | 0 (a blank cell sorts as 0 in numeric context; no error documented) | blank — the exported cell carried no value and no error | #NAME? | 0 (24.8.7.2) → #VALUE! (25.2.0.3) → 0 (25.8.7.3) |
Row two is the whole page in one line. The Excel column and the LibreOffice column both read
5, 4, 3, 1, 1; the Google Sheets column reads 1, 1, 3, 4, 5 — the
identical output the ascending case produced one row above. Sheets did not refuse the
-1, did not warn about it, and did not return a partial result. It sorted ascending.
Row three is the accidental agreement. =SORT(A1:B3,2,1) asks for “sort by the
second column, ascending” in Excel’s dialect, and the same 1 is a truthy
is_ascending in Sheets’ dialect, so both engines return
y, 10 / z, 20 / x, 30. It is the same coincidence that makes the divergence hard to
find: a workbook full of ascending sorts imports perfectly and tells you nothing about the
descending ones.
Reading “-1 is nonzero, therefore truthy, therefore TRUE,
therefore ascending” is our explanation of the measurement, not itself a measurement. What we
executed is the value: 1, 1, 3, 4, 5, where 5, 4, 3, 1, 1 was asked for.
The #NAME? column is a separate story: SORT does not exist in
LibreOffice 24.2.0.3 at all, and arrives in 24.8.7.2. That version map, for SORT and
the rest of the dynamic-array family, is in
which LibreOffice
version you need for VSTACK, HSTACK, TEXTSPLIT, TAKE and DROP.
Executed: the same collision in two how-to recipes
Our how-to corpus is a second, independent body of executed formulas, run in LibreOffice Calc
25.8.7.3 and imported to Google Sheets on 2026-08-30. Two of its pages turn on this argument. The
gs-alt rows are Google Sheets alternatives: formulas scoped to that engine in our
corpus and executed there only, which is why their LibreOffice cell reads n/a rather
than a value.
Inputs: the sort recipe reads A2:A4 = 20, 50, 10; the reverse recipe reads
A2:A4 = apple, banana, cherry.
| Recipe | Formula | Excel (documented) | Returned by Google Sheets (executed 2026-08-30) | LibreOffice 25.8.7.3 (executed) |
|---|---|---|---|---|
| sort-a-range-with-a-formula main | =SORT(A2:A4,1,-1) | 50, 20, 10 | 10, 20, 50 | 50, 20, 10 |
| sort-a-range-with-a-formula gs-alt | =SORT(A2:A4,1,FALSE) | — Sheets-only rewrite, no Excel claim | 50, 20, 10 — Google Sheets alternative (executed 2026-08-30) | n/a (Sheets-only formula) |
| reverse-the-order-of-a-list main | =SORTBY(A2:A4,ROW(A2:A4),-1) | cherry, banana, apple | #NAME? | cherry, banana, apple |
| reverse-the-order-of-a-list gs-alt | =SORT(A2:A4,ROW(A2:A4),FALSE) | — Sheets-only rewrite, no Excel claim | cherry, banana, apple — Google Sheets alternative (executed 2026-08-30) | n/a (Sheets-only formula) |
The fix is one argument, and we ran it. Replace -1 with
FALSE: =SORT(A2:A4,1,FALSE) returned 50, 20, 10 in the same
Google Sheets run that gave 10, 20, 50 for the -1 form. Nothing else about
the formula changes — same range, same sort column, same spill.
The reverse-a-list pair shows the argument doing a second job. SORTBY is the natural
Excel and LibreOffice way to flip a list without alphabetising it — sort the range by its own
row numbers, descending — and it executed to cherry, banana, apple in Calc
25.8.7.3. In Google Sheets it returned #NAME?, which is a real result rather than an
import artefact: our function inventory records SORTBY as undocumented in Sheets, and
the corpus’s own =SORTBY(A1:A3,B1:B3) case likewise returned #NAME?
there on 2026-08-29. The Sheets rewrite is SORT itself with a range as its second
argument — =SORT(A2:A4,ROW(A2:A4),FALSE) — because Google documents
sort_column as either a column index or “a range outside of
range containing the values by which to sort”. That executed to
cherry, banana, apple. So Sheets can sort by an arbitrary key; it just spells it
SORT, and the direction flag is still the boolean.
Google also ships SORTN, a top-n sorter that Excel and LibreOffice do not
have at all. Our inventory records it as documented in Google Sheets only, and we have authored no
test cases for it — it is mentioned here as documentation, with no executed result behind
it.
The blank-cell case, and one LibreOffice release that disagreed with itself
The fourth corpus case, =SORT(A1:A1) over a single empty cell, is not about the
third argument, but it is the other thing our SORT runs turned up and it is worth
recording honestly.
In LibreOffice it is the only cross-release instability in the whole executed corpus. Our
LibreOffice version support page reports that of
791 non-volatile cases across 250 functions that ran in all four tested builds, exactly one did not
return the identical value in every build after the first that supported it — and it is this
one: #NAME? in 24.2.0.3, 0 in 24.8.7.2, #VALUE! in 25.2.0.3,
and 0 again in 25.8.7.3. A degenerate input regressed for one release and came back.
“Supported” is per case, not per function.
In Google Sheets the same case came back as an empty cell: our readback recorded no value and no
error code for it, against a documented 0. Be careful how much weight that carries. Our
Sheets measurement is an export-and-readback — the workbook goes to Drive, Google recalculates
it, and the file comes back as .xlsx — and an exported cell holding nothing is
indistinguishable from a formula that produced nothing. We can say the cell contained no value and
no error; we cannot separate “SORT returned blank” from “the export
wrote no cell”. What we can say is that the sheet did recalculate: its arithmetic canary
passed. Treat it as blank-not-zero with that caveat attached, and do not build on it.
Finding this in a workbook before it costs you
Search the formula text for ,-1) next to SORT. That is
the entire signature of the bug. A workbook moving from Excel or LibreOffice to Google Sheets needs
every SORT(...,-1) rewritten to SORT(...,FALSE); every
SORT(...,1) and every argument-less SORT(range) can be left alone, on the
evidence above.
Do not trust the eyeball test on a sorted column. A reversed sort still looks
like a sorted list. If the column is a leaderboard, the top row is now the worst performer; if it
feeds an INDEX/MATCH or a “first row” reference, the number
downstream is wrong and plausible. Check the first value against the maximum, not against your
sense of tidiness.
Going the other way is a hard failure, which is better. A Sheets formula written
=SORT(A2:A4,1,FALSE) and opened in Excel or LibreOffice hands a boolean to an argument
documented to take a number. We have no executed case for that direction — our corpus does not
run Sheets-dialect formulas in Calc — so we make no claim about what it returns, only that the
rewrite in this page’s table is scoped to Sheets on purpose and is not portable back.
Rebuild descending sorts on SORTBY only if the target has it.
SORTBY works in LibreOffice from 24.8.7.2 in our runs and returns #NAME?
in Google Sheets, so it is a fine Excel/Calc idiom and a dead end for Sheets. The portable-by-hand
alternative for reversing a list, recorded on the how-to page, is an
INDEX-with-COUNTA fill-down.
This belongs to the same family as the other quiet Google Sheets divergences we have executed:
Google
Sheets does not array-evaluate inside a scalar function, where four of the divergent rows come
back as plain wrong numbers, is the largest one. SORT’s third argument is
narrower but sharper: there is no error to catch anywhere in the chain, and the result is still a
correctly-formed, correctly-typed, fully-populated column.
Honest limits
The Excel column throughout is Microsoft’s documented behaviour as recorded in our test corpus. We do not run Excel, and no value in that column is a measurement.
The Google Sheets columns are executed output from dated runs, because Sheets has no version
number to pin. The function-corpus values carry the label Google Sheets (Drive import, 2026-08-29);
the recipe values come from the 2026-08-30 run of the recipe corpus. In both, a formula-only .xlsx
with no cached results goes to Google Drive, is recalculated by Google’s engine, and is
exported back for readback, with a deterministic =1111+2222 canary proving the
recalculation happened. Google could change any of this tomorrow without changing a version
number.
The Sheets cases on this page were written into the .xlsx with plain function names, while the
LibreOffice reference run executed the _xlfn. storage form of the same formulas. The
formula text is identical, but the two runs did receive different input files, and our results
record that note per case. That serialization difference is why the 2026-08-29 Sheets values for
SORT exist at all: an earlier prefixed run left them inconclusive.
The LibreOffice columns are executed output from four builds — 24.2.0.3, 24.8.7.2, 25.2.0.3 and 25.8.7.3 — and those four are the only ones we tested. A behaviour we describe as arriving in 24.8.7.2 may have arrived in any release after 24.2.0.3.
Finally, scope. Everything above is measured on the three-argument form of SORT over
small ranges. We have no executed case for the fourth argument, for multiple sort keys, for
sort_column ranges in Excel or LibreOffice, or for what a boolean does to
sort_order outside Sheets. See our methodology for how
recalculation is proven rather than assumed, and
the SORT function page for the per-engine support matrix.