Your function exists, but your file cannot say so
Every compatibility question about a spreadsheet function has two halves, and almost everyone only asks the first. Does the engine implement this function? is the famous one. Can the file format name it? is the one that actually decides whether your workbook opens.
The OOXML spreadsheet format was frozen at the Excel 2007 function set. Everything added since
— and that is now most of the interesting library — is stored in a
.xlsx not under its own name but under a prefixed token: _xlfn.XLOOKUP,
_xlfn._xlws.FILTER, _xlfn.T.INV.2T. Excel adds the prefix when it saves and
strips it when it displays, so you never see it. Every other engine has to recognise the token, and
where it does not, you get #NAME? for a function that engine can compute perfectly
well.
The surprise
LibreOffice’s transport layer is lossy for LibreOffice itself, in three separate
ways. It cannot read the token Excel writes for eight complex-number functions it fully implements.
Its own .xlsx exporter silently collapses eleven of its own legacy function names onto
other functions’ tokens, so those names cannot survive a save at all. And the token it writes
for eight more of its own functions changed between 24.2 and 24.8, which means a
file saved by LibreOffice 24.8 or later opens in LibreOffice 24.2 as #NAME? — a
forward incompatibility inside one application’s own file format.
How we know, and what “probed” means here
This guide is unusual for this site in that its evidence is not only the executed corpus but the
measurements that had to be made before the corpus could run. To execute a function honestly
you must first write it into a workbook under a token the engine will read; get that wrong and you
publish “unsupported” for a function that works. So every entry in
harness/xlfn_map.py was measured in both directions on all four pinned builds
(24.2.0.3, 24.8.7.2, 25.2.0.3, 25.8.7.3): import, by writing each candidate spelling
into a workbook with openpyxl (which caches no value, so the engine must evaluate from scratch) and
round-tripping it through soffice --headless --convert-to xlsx; and export,
by feeding the same formulas to each build’s own parser and reading the stored token straight out
of the raw sheet XML. Up to nine spellings were tried per function — plain, _xlfn.,
COM.MICROSOFT., ORG.OPENOFFICE., _xlfn.ORG.OPENOFFICE.,
ORG.LIBREOFFICE., _xlfn.ORG.LIBREOFFICE., _xlfn.COM.MICROSOFT.
and COM.SUN.STAR.SHEET.ADDIN.ANALYSIS.
The map now holds 165 prefixed storage tokens. 164 of them have
executed test cases, and of those 137 evaluate in LibreOffice Calc 25.8.7.3 under the prefixed
token — that is, the prefix is doing exactly its job and the function works. The other
27 return #NAME?, and the interesting question is which of those 27 are missing functions
and which are missing mappings.
1. Eight functions LibreOffice has and cannot be told about
Microsoft’s XLSX-extensions future-function list gives the storage token for the Excel 2013
complex-number additions as _xlfn.IMCOSH, _xlfn.IMCOT,
_xlfn.IMCSC, _xlfn.IMCSCH, _xlfn.IMSEC,
_xlfn.IMSECH, _xlfn.IMSINH and _xlfn.IMTAN. That is what real
Excel writes, so that is what our harness writes.
| Formula (as typed) | Excel (documented) | Google Sheets (executed 2026-08-31) | LibreOffice Calc 25.8.7.3 (executed, _xlfn. token) |
|---|---|---|---|
| =IMCOT("4+3i") | 0.00490118239430447-0.999266927805902i | 0.00490118239430447-0.999266927805902i | #NAME? |
| =IMSEC("4+3i") | -0.065294027857947-0.0752249603027732i | -0.065294027857947-0.0752249603027732i | #NAME? |
| =IMTAN("4+3i") | 0.00490825806749606+1.00070953606723i | 0.00490825806749606+1.00070953606723i | #NAME? |
| =IMSINH("4+3i") | -27.0168132580039+3.85373803791938i | -27.0168132580039+3.85373803791938i | #NAME? |
| =IMCOSH("4+3i") | -27.0349456030742+3.85115333481178i | -27.0349456030742+3.85115333481178i | #NAME? |
| =IMCSC("4+3i") | -0.0754898329158637+0.0648774713706355i | -0.0754898329158637+0.0648774713706355i | #NAME? |
| =IMCSCH("4+3i") | -0.036275889628626-0.0051744731840194i | -0.036275889628626-0.0051744731840194i | #NAME? |
| =IMSECH("4+3i") | -0.0362534969158689-0.00516434460775318i | -0.0362534969158689-0.00516434460775318i | #NAME? |
All 24 cases across the eight functions returned #NAME? on all four builds. That looks
like eight unsupported functions, and it is not. The five-spelling probe recorded in each test file
settles it: _xlfn.IMCOT, COM.MICROSOFT.IMCOT,
ORG.OPENOFFICE.IMCOT and _xlfn.ORG.OPENOFFICE.IMCOT are all
#NAME? on every build, while the unprefixed IMCOT(…) evaluates
on every build and returns exactly the documented string, byte for byte. LibreOffice
implements all eight correctly. Its OOXML import simply has no mapping for the token Excel writes.
So the practical statement is the uncomfortable one: an Excel workbook that uses
IMCOT arrives in LibreOffice as #NAME?, even though LibreOffice could compute
IMCOT. Retyping the formula in LibreOffice makes it work. Saving that file back to
.xlsx and reopening it does not.
2. The same mechanism, running the other way, for their real-number cousins
COT, COTH, CSC and CSCH are the direct
trigonometric siblings of the functions above, added to Excel in the same release. For them the
mapping exists and it is the only spelling that works:
| Formula (as typed) | Excel (documented) | Google Sheets (executed 2026-08-31) | LibreOffice Calc 25.8.7.3 (executed, _xlfn. token) |
|---|---|---|---|
| =ROUND(COT(30),9) | -0.156119952 | -0.156119952 | -0.156119952 |
| =ROUND(CSC(15),9) | 1.537780562 | 1.537780562 | 1.537780562 |
| =ROUND(SEC(45),5) | 1.90359 | 1.90359 | 1.90359 |
| =ROUND(T.INV.2T(0.05,10),9) | 2.228138852 | 2.228138852 | 2.228138852 |
Written under the bare name, all four of those are #NAME? in LibreOffice; written under
_xlfn., they compute. The map records more than twenty such cases — the four
trigonometric ones, SEC, SECH, SKEW.P, the six-strong
T.DIST/T.INV family, T.TEST, WEIBULL.DIST,
Z.TEST, MUNIT, NEGBINOM.DIST, PHI and
POISSON.DIST. Each of them would have been published as “unsupported in
LibreOffice” by a harness that wrote the plain name, and each of them works.
BAHTTEXT is the odd one in this group and worth knowing about because no vendor table
predicts it: it predates 2007 and is absent from the public future-function list, but
LibreOffice’s own OOXML function table tags it as stored-as-macro-call, and empirically
=_xlfn.BAHTTEXT(1234) returns the exact Thai string Microsoft documents on all four builds
while =BAHTTEXT(1234) is #NAME? on all four.
ISO.CEILING goes the other way and is the reason this map is measured rather than
copied. It is a post-2007 function, but Microsoft’s own storage list spells exactly four
future functions without the prefix — ECMA.CEILING, ISO.CEILING,
NETWORKDAYS.INTL and WORKDAY.INTL — while its 155 neighbours carry
_xlfn.. LibreOffice’s own filter draws the same line. Empirically, on all four
builds =ISO.CEILING(4.3) returns 5 while =_xlfn.ISO.CEILING(4.3) and
=COM.MICROSOFT.ISO.CEILING(4.3) are #NAME?, and round-tripping through
LibreOffice’s own export writes the bare token back. An earlier version of our map had this
entry wrong, which would have published a false “unsupported” verdict; the export
measurement is what caught it.
3. Eleven LibreOffice names its own exporter erases
LibreOffice keeps a set of legacy aliases — the Analysis-ToolPak-era _ADD forms,
the _EXCEL2003 forms, and two others. They exist, they compute, and they have no reachable
OOXML token at all, because LibreOffice’s own .xlsx export collapses each of
them onto another function’s name:
| LibreOffice name | Token LibreOffice writes on export | Result of every import spelling probed |
|---|---|---|
| CUMIPMT_ADD | CUMIPMT | #NAME? on all four builds |
| CUMPRINC_ADD | CUMPRINC | #NAME? on all four builds |
| EFFECT_ADD | EFFECT | #NAME? on all four builds |
| NOMINAL_ADD | NOMINAL | #NAME? on all four builds |
| GCD_EXCEL2003 | GCD | #NAME? on all four builds |
| LCM_EXCEL2003 | LCM | #NAME? on all four builds |
| ISEVEN_ADD | ISEVEN | #NAME? on all four builds |
| ISODD_ADD | ISODD | #NAME? on all four builds |
| WEEKNUM_EXCEL2003 | WEEKNUM | #NAME? on all four builds |
| FORMULA | _xlfn.FORMULATEXT | #NAME? on all four builds |
| SKEWP | _xlfn.SKEW.P | #NAME? on all four builds |
Nine spellings were probed for each; all nine are #NAME? on all four builds, and the
export direction explains why. Save a .xlsx from LibreOffice and the alias is simply gone,
replaced by its modern namesake. For most of the eleven that substitution is harmless —
ISEVEN_ADD and ISEVEN do the same thing — but it is worth knowing that
the name in your formula bar is not the name in your file, and that a formula audit tool reading the
XML will see the modern function, not the one you wrote.
This is also why you will not find these eleven in our function corpus with a verdict attached.
There is deliberately no test file for any of them: writing CUMIPMT into a workbook tests
CUMIPMT, not CUMIPMT_ADD, and a #NAME? run would have published
“unsupported in LibreOffice” for eleven functions LibreOffice genuinely implements. They
appear on our coverage list as documented skips, with the
reason, rather than as a verdict we are not entitled to.
4. A LibreOffice file that LibreOffice cannot open
This is the finding that only running four builds could produce. Eight LibreOffice-only functions
— DAYSINMONTH, DAYSINYEAR, ISLEAPYEAR,
MONTHS, ROT13, WEEKS, WEEKSINYEAR and
YEARS — changed their OOXML export token between 24.2 and 24.8:
| Build | Token it writes on export | Tokens it accepts on import |
|---|---|---|
| LibreOffice 24.2.0.3 | ORG.OPENOFFICE.ROT13 | ORG.OPENOFFICE.ROT13 only — the bare name is #NAME? |
| LibreOffice 24.8.7.2 | ROT13 | both |
| LibreOffice 25.2.0.3 | ROT13 | both |
| LibreOffice 25.8.7.3 | ROT13 | both |
Read that table in the direction that hurts. A colleague on 24.8, 25.2 or 25.8 saves an
.xlsx containing =ISLEAPYEAR(A1). The file writes the bare token. You open it
on 24.2 — still shipping in long-support distributions — and the formula is
#NAME?, in the same application, for a function both builds implement. (A curiosity from
the same probe: on 24.2 the bare =ROT13(...) comes back #VALUE! rather than
#NAME?, so even the error does not tell you what happened.) Our map records the
ORG.OPENOFFICE. form precisely because it is the one all four builds read; using the bare
name would have produced eight false “unsupported in LibreOffice 24.2” verdicts.
All eight functions themselves are fine. Executed on 2026-09-01 under the token all four builds
accept, they return the documented values on every build —
=ROT13("Gur Qbphzrag Sbhaqngvba jnf sbhaqrq va Frcgrzore 2010.") gives back
“The Document Foundation was founded in September 2010.”,
=ISLEAPYEAR(DATE(1968,2,29)) gives 1 and =ISLEAPYEAR(DATE(2100,1,1)) gives 0.
Google Sheets has none of the eight; all of their cases are #NAME? there, which is an
honest unsupported verdict rather than a transport problem.
The namespace rule underneath all this is vintage: functions inherited from OpenOffice.org take
ORG.OPENOFFICE., functions LibreOffice added after the fork take
ORG.LIBREOFFICE.. It is not applied consistently. EASTERSUNDAY and
ERRORTYPE are OpenOffice.org-era yet carry _xlfn. on top of
ORG.OPENOFFICE. on all four builds, where their eight neighbours above do not.
CONVERT_OOO is not prefixed but renamed, to
_xlfn.ORG.OPENOFFICE.CONVERT — no prefix rule can express a rename, which is why our
map stores whole tokens. RAND.NV and RANDBETWEEN.NV are reachable under
exactly one of nine spellings each, and the bare name is not it. CURRENT answers to both
the bare name and the prefixed one but is always written back prefixed. DDE answers only
to the bare name and is written back bare.
5. Google’s importer has the same class of gap
This is not a LibreOffice-only phenomenon, and it is worth saying so plainly. Our first Google
Sheets run wrote the same Excel-authored workbooks, prefixes and all, and Google’s importer maps
some prefixes but not others: _xlfn.XLOOKUP, _xlfn.MAP and
_xlfn.LAMBDA evaluated fine with the prefix, while
_xlfn._xlws.FILTER and _xlfn._xlws.SORT came back #NAME? and
BYROW, BYCOL and MAKEARRAY came back #ERROR!
— for five functions Google documents and implements. We published those as
inconclusive rather than unsupported, then re-ran the same corpus with plain,
unprefixed names on 2026-08-29, which resolved all five into real executed verdicts. The two runs are
merged into one results file with per-function provenance for which run produced which verdict. The
general caveat stands for anything we have not re-run that way, and it is written up in the
Google Sheets execution caveats.
What to do about it
Diagnose #NAME? by retyping, not by searching. If a formula shows
#NAME? after opening a file, delete the cell contents and type the same formula in by
hand. If it now computes, you have a storage-token problem, not a missing function, and the fix is a
file-level one. If it still fails, the function really is absent in that engine.
Do not write raw OOXML with plain modern function names. This bites hardest outside
the office suites entirely: openpyxl, XlsxWriter and every other library that writes sheet XML directly
will happily store =XLOOKUP(...) verbatim, and then both Excel and LibreOffice
show #NAME? for a function both support. That is the single most common false
“compatibility bug” in generated spreadsheets, and it is a serialization bug in the
generator.
Treat a version floor as a file-format question, not just a feature question. The 24.2/24.8 split above means “everyone is on LibreOffice” is not sufficient for a shared workbook. If any of those eight functions is in the file, everyone needs to be on 24.8 or later, or the file needs to avoid them.
Prefer the portable spelling when one exists. Where a LibreOffice-only alias has a modern namesake — and for nine of the eleven in section 3 it does — using the modern name costs nothing and survives every round trip, because it is what the file was going to say anyway.
Honest limits
The Excel column throughout is Microsoft’s documented behaviour and Microsoft’s published storage-token list, as recorded in our test corpus. We do not run Excel, and no value in that column is a measurement — including the claim that Excel writes a given token, which comes from the published extensions list rather than from a file we saved from Excel.
The Google Sheets column is executed output from dated runs, because Sheets has no version number to
pin: a formula-only .xlsx carrying no cached results goes to Google Drive, is recalculated
by Google’s engine, and comes back as .xlsx for readback, with a deterministic
=1111+2222 canary in every sheet proving the recalculation happened. The complex-number
and trigonometric cases in sections 1 and 2 ran on 2026-08-31; the LibreOffice-only functions in
section 4 ran on 2026-09-01; the prefix-mapping observations in section 5 date from the 2026-08-29
runs.
The LibreOffice column is executed output from Calc 25.8.7.3 unless a row names another build, and every corpus case cited here returned identical results on 24.2.0.3, 24.8.7.2 and 25.2.0.3 — except, by construction, the 24.2 import behaviour in section 4, which is the finding. Those four builds are the only ones we tested, so “the token changed between 24.2 and 24.8” means exactly that and not that we have bisected which release made the change.
The spelling probes throughout — the five-spelling and nine-spelling sweeps, the export-token
readings out of raw sheet XML, and the eleven collapsed aliases — are measurements recorded in
harness/xlfn_map.py rather than rows in the published corpus. They are reproducible the
same way and by the same commands, but they are not test cases with verdicts, and the eleven aliases
deliberately have no verdict at all.
Scope. This guide covers the tokens our corpus needed, which is 165 prefixed names plus 17
LibreOffice-only ones. Untested here: every OOXML function name we had no reason to write, defined
names and LAMBDA parameters (which use a third prefix, _xlpm.), the legacy
.xls binary format, ODF as a transport, and what Excel itself does when it opens a file
LibreOffice wrote. See our methodology for how recalculation is proven
rather than assumed, and the function pages for
IMCOT,
COT,
ISO.CEILING and
ROT13 for the per-engine support matrices.