Excel Office.js add-in: WorksheetProtection.unprotect() throws AccessDenied (403) on Desktop
06:56 24 Jun 2026

Starting around mid-June 2026, our Excel Office add-in began failing on WorksheetProtection.unprotect().
The call throws:

RichApi.Error: AccessDenied — "You cannot perform the requested operation."
code: "AccessDenied"
httpStatusCode: 403
errorLocation: "WorksheetProtection.unprotect"

We did not deploy any change. Office.js is loaded from Microsoft's CDN, so this started for many users simultaneously — pointing to an Excel/Office.js update, not our code. protect(), delete(), and cell value/format writes all still work; only unprotect() is rejected.

Minimal reproduction

This fails even on a brand-new, natively-created sheet (no insertWorksheetsFromBase64 involved):

await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.add();
    await context.sync();

    sheet.protection.protect({}, "myPassword"); // ✅ succeeds
    await context.sync();
  });

  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getItem(/* the new sheet */);
    sheet.protection.unprotect("myPassword");   // ❌ AccessDenied (403)
    await context.sync();
  });

The exact same password works when unprotecting manually via Review → Unprotect Sheet.

What I've already tried (all still fail)

  • unprotect(password), unprotect(""), and unprotect() (no arg)
  • Re-fetching the sheet by id/name inside a fresh Excel.run
  • Parameterless await context.sync() (vs context.sync(sheet)) — no difference
  • Code-applied protection (protecting then immediately unprotecting in the same session)
  • Brand-new worksheets.add() sheet, and sheets inserted via insertWorksheetsFromBase64 — both rejected
  • pauseProtection() before unprotect() — throws NotImplemented (501) on Desktop; it appears to be ExcelApiOnline (web-only)

Environment

Questions

1. Is this an intentional Office Desktop security/hardening change, or a regression?
2. Is there a supported way to programmatically remove worksheet protection from an add-in now, given pauseProtection() is not implemented on Desktop?
3. If unprotect() is no longer available to add-ins, is the recommended pattern now "protect once with specific cells unlocked, and never unprotect"?

office-js