Skip to content

LTSC 2024 Mac WefProcess rejects manually-registered dev catalog #1008

Description

@zsy619

Summary

On Office LTSC 2024 Mac, Excel's WEF scanner has a hard gate that
silently strips externally-written BlockForMinor,
EntitlementsInvalid, and AppStates values from any catalog with
UniqueId="developer" on the next Excel launch. The result is that
the dev add-in can never be promoted to a WefProcess no matter
how completely the SQL row set + on-disk layout is backfilled by hand
or by tooling.

Environment

  • Office LTSC Standard for Mac 2024, version 16.110.2, build 26062818
  • macOS 15.5
  • Any add-in registered with office-addin-debugging (so
    UniqueId="developer")
  • SQLite 3 access to
    ~/Library/Group Containers/UBF8T346G9.Office/MicrosoftRegistrationDB/MicrosoftRegistrationDB_19413899015.reg

Repro

# Pre: Excel is NOT running.
# Pre: dev catalog is registered (via office-addin-debugging start or
#      equivalent). The catalog is at SQL node_id 17763 with
#      UniqueId=developer.

# 1. Verify pre-state
sqlite3 "$DB" "SELECT name, type FROM HKEY_CURRENT_USER_values WHERE node_id=17763;"
# Expected: Entitlements, UniqueId  (2 rows)

# 2. Backfill the 5 preconditions manually (full SQL + on-disk layout)
#    (use any method: Python sqlite3, a third-party reg tool, etc.)
#    After this step, dev catalog has: BlockForMinor, Entitlements,
#    EntitlementsInvalid, UniqueId, AppStates child node with
#    <aid>_<ver> value, AddinLifecycle OMEX_<cat>_<aid>_{1,2,4} entries.
sqlite3 "$DB" "SELECT name, type FROM HKEY_CURRENT_USER_values WHERE node_id=17763;"
# Now: 4 rows (BlockForMinor, Entitlements, EntitlementsInvalid, UniqueId)
sqlite3 "$DB" "SELECT node_id, name FROM HKEY_CURRENT_USER WHERE parent_id=17763;"
# Now: 1 child (AppStates)
ls -la "$WEF/<catalog-hash>/"
# Now: 10 subdirs (AppStates, AppDetails, ExtendedManifest, ...)

# 3. Launch Excel
open -a "Microsoft Excel"
sleep 15

# 4. Quit Excel
osascript -e 'tell application "Microsoft Excel" to quit'
sleep 5

# 5. Re-check SQL state
sqlite3 "$DB" "SELECT name, type FROM HKEY_CURRENT_USER_values WHERE node_id=17763;"
# Result: back to 2 rows (Entitlements, UniqueId) — preconditions stripped!
sqlite3 "$DB" "SELECT node_id, name FROM HKEY_CURRENT_USER WHERE parent_id=17763;"
# Result: 0 children — AppStates gone
ls -la "$WEF/<catalog-hash>/" 2>/dev/null
# Result: No such file or directory — on-disk layout deleted

# 6. Excel diagnostic log
#    Path: ~/Library/Containers/com.microsoft.Excel/Data/Library/Logs/Diagnostics/EXCEL/Primary*.log
#    Grep for the addin GUID or catalog hash: ZERO references.
#    No WefProcess child was ever spawned during the entire 15s session.

Expected

After manually writing all 5 preconditions and recreating the on-disk
WEF layout, the dev catalog should be discoverable by Excel's WEF
scanner. WefProcess should spawn, the manifest should be read, and
the add-in should load.

Actual

Excel launches normally, no WefProcess child is created, the dev
server gets zero requests, and the diagnostic log is silent. After
Excel quits, the SQL rows I wrote (BlockForMinor,
EntitlementsInvalid, the AppStates child node, the per-addin
AppStates value) and the on-disk layout are all gone. Only the
Entitlements and UniqueId="developer" rows survive.

The same SQL writes + on-disk layout, if applied to a catalog with
UniqueId set to a value other than "developer", do persist across
Excel launches (verified by diffing against the Omex catalog
geLWvPTNy9pxk4xxzFEGHw== which has UniqueId=842d1cbc0a4605aa_LiveId
and retains all 4 values + AppStates child across launches).

Diagnosis

The WEF scanner on LTSC 2024 Mac has a hardcoded trust gate keyed off
UniqueId (or some adjacent value we haven't yet isolated). For
catalogs it doesn't trust (which includes any catalog with
UniqueId="developer"), it:

  • refuses to install the manifest into the WEF pipeline
  • silently strips the precondition rows on the next launch
  • deletes the on-disk mirror
  • spawns no WefProcess

This is not a check for cert chain / manifest signature / Entitlments
GUID validity (those are addressed in the related issue). The gate
fires before any of those checks.

The exact value of the gate has not been identified. Most likely
candidates (in order):

  1. UniqueId="developer" is on a hardcoded deny-list
  2. A CatalogSource REG_DWORD bit we haven't located in the SQL tree
  3. An Apple SecCode/SecTrust check on the Excel binary that the
    dev catalog's child process would inherit

The CustomUIValidationCache value
e504fb41-..._developer.Microsoft.Excel.Workbook = 1099998353 does
survive and is present even before any backfill, so the manifest XML
itself is being parsed and validated by Excel.

Workaround tried

  • Tried backfilling all 5 preconditions + on-disk layout before
    launching Excel: still gets stripped on the first launch
  • Tried backfilling the on-disk layout to live at
    Wef/{EC993EB3-...}/Omex/<catalog-hash>/ (Omex-style nested path):
    same result
  • Tried mirroring the manifest both at the on-disk path and as a
    fs.ensureLinkSync at the legacy ~/Documents/wef/: legacy path
    is no longer read by LTSC 2024 Mac
  • Tried flipping DebugAddins=1 at
    HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Excel\Options:
    no effect on dev catalog visibility
  • Tried office-addin-debugging@5.1.66.1.1 upgrade: byte-equivalent
    Mac write path (dev-settings-mac.js identical between
    office-addin-dev-settings@2.3.6 and 3.1.1)

Related

  • See also: office-addin-debugging 5.x / 6.x doesn't sideload on LTSC 2024 Mac (office-addin-debugging 5.x / 6.x doesn't sideload on LTSC 2024 Mac #1006) — this issue is the broader "tooling only
    writes 2/6 SQL rows" framing of the same root cause
  • See also: Omex manifest signing not reproducible for non-Microsoft
    add-ins (issue TBD) — the signature question is downstream of this
    gate and is only relevant after this gate is bypassed

Additional verification rounds (2026-07-03, 12:00–12:04)

After filing the report above, I ran 3 more rounds to isolate the
gate. All 3 made incremental improvements but none unblocked
WefProcess spawn
.

Round v2: decimal-string format for QWORD-like values

Discovery: the HKEY_CURRENT_USER_values.value column stores
decimal string representations of 8-byte integers, not raw
bytes — even when type=1 (REG_SZ) or type=11 (REG_QWORD).
E.g. the working Omex baseline has:

OMEX_geLWvPTNy9pxk4xxzFEGHw==_geLWvPTNy9pxk4xxzFEGHw==_1|1|19|8307396822173121467
Entitlements|11|18|134276099564810000

I had been writing 20 bytes of X'00' (raw zero) which the
scanner rejects. After re-writing the AppStates value and the
3 AddinLifecycle entries as decimal FILETIME strings
(e.g. 134275249473009060):

  • All 5 SQL preconditions SURVIVED the launch (first time)
  • WefProcess still did not spawn
  • Dev server still got 0 requests
  • New on-disk catalog still not created

Round v3: AddinLifecycle naming convention

Discovery: the 3 OMEX_*_*_{1,2,4} entries are catalog-level
(not addin-level). The format is
OMEX_<catalog-hash>_<catalog-hash>_<N>, with the catalog-hash
repeated, not the AID. My initial writes used
OMEX_<catalog-hash>_<aid>_<N> which is wrong.

After re-writing the 3 entries with the correct naming
OMEX_vai+V16+KDygsa7v22t_6Q==_vai+V16+KDygsa7v22t_6Q==_{1,2,4}:

  • All 5 SQL preconditions survived (second time)
  • WefProcess still did not spawn

Round v4: UniqueId=OmexAnonymous (anonymous mode)

Discovery: this Office install is running anonymously
(UserIdentityCache node 14680 has ExcelIsAnonymous=1 and
ExcelOmexUserIdentity=""). The 3 catalog UniqueId values
on the system are:

  • Omex (14682): 842d1cbc0a4605aa_LiveId (synthetic LiveId, 20 chars)
  • Omex anonymous (14683): OmexAnonymous (14 chars)
  • Dev (17763): developer (9 chars, what office-addin-debugging writes)

Changed dev catalog's UniqueId from developer to
OmexAnonymous to match the anonymous-mode Omex catalog:

  • All 5 SQL preconditions survived (third time)
  • WefProcess still did not spawn
  • Dev server still got 0 requests

Conclusion after 4 rounds

The WefProcess gate is not in any of the following, all of
which I have now written and confirmed surviving the launch:

  • SQL BlockForMinor=0 (REG_DWORD)
  • SQL EntitlementsInvalid={guid} (REG_SZ, valid GUID)
  • SQL AppStates subtree with <aid>_<ver> REG_QWORD FILETIME value
  • SQL AddinLifecycle 3 entries with correct naming and decimal-string values
  • On-disk 5 per-addin files: AppStates/, Tokens/, Html/,
    Metadata/, TrustedApps/
  • UniqueId format (developer vs OmexAnonymous — neither works)

The remaining gate is upstream of SQL + on-disk. Most likely
candidates (in order):

  1. Certificate trust chain — XAdES-T signed by
    CN=Microsoft Corporation Third-Party App Distribution
    (Microsoft-internal cert, see the related Omex-signing issue)
  2. WebView2 trusted domain plist entry for 127.0.0.1:3000
  3. CatalogSource bit in a SQL key not yet located
  4. Apple entitlement / SecTrust check on the dev catalog
    process

Diagnostic tooling gap: no WefProcess verbose log mode is exposed
to users. Microsoft-internal ETW events would help diagnose which
of these gates is firing, but they're not accessible from the
user-side.

Backups preserved (in case anyone wants to replicate):

  • SQL pre-restore: MicrosoftRegistrationDB_19413899015.reg.bak.pretest.1783047857
  • SQL pre-Plan-B+C: /tmp/plan-bc-pre-1783050972.bak
  • On-disk pre-Plan-C: /tmp/plan-c-backup-1783050724.tar.gz

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions