Skip to content
Back to skills

Excel Connector Governance

BSecurity

Governs driving Excel Online (Business) through a Microsoft Copilot Studio agent against SharePoint-hosted .xlsx. Triggers — "Copilot Studio", "Excel Online (Business)", "add a row", "update a row", "delete a row", "connector 502 / BadGateway", "Office Script", "pin the File input", "spreadsheet agent", "SharePoint xlsx agent". Loudest bug — the File pin stores an opaque 34-character drive-item ID and shows no filename anywhere in the UI, so a tool pinned to the wrong workbook fails identical...

  • 6 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 5, 2026
ai-agentsrustgotestinggitapidocumentation

Works with

  • cli
  • api

Security analysis

B88/100
  • criticalAccesses sensitive system or user directories

Pro shows the line behind each finding and how to fix it

Scanned October 5, 2026

npx -y skills add shawnclybor/clybor-claude-tooling --skill excel-connector-governance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Excel Connector Governance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Excel Connector Governance
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/shawnclybor-excel-connector-governance/badge)](https://www.skillsdirectory.com/skills/shawnclybor-excel-connector-governance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: excel-connector-governance
description: Governs driving Excel Online (Business) through a Microsoft Copilot Studio agent against SharePoint-hosted .xlsx. Triggers — "Copilot Studio", "Excel Online (Business)", "add a row", "update a row", "delete a row", "connector 502 / BadGateway", "Office Script", "pin the File input", "spreadsheet agent", "SharePoint xlsx agent". Loudest bug — the File pin stores an opaque 34-character drive-item ID and shows no filename anywhere in the UI, so a tool pinned to the wrong workbook fails identically every time, and against a healthy workbook would silently write to the wrong client's file. Resolve every File pin back to a filename before trusting a write. Cheap to consult; expensive to write a row into the wrong client's workbook, or to record a working connector as a permanent limitation.
---

# Excel Connector Governance (Copilot Studio → Excel Online → SharePoint)

Governance for driving **Excel Online (Business)** through a **Microsoft Copilot Studio**
agent against `.xlsx` files hosted in **SharePoint**. Loaded on demand — never auto-loaded.

Every claim below was verified live against a real tenant on **2026-08-14** unless marked
`UNVERIFIED (doc-sourced)`. Several of these findings contradict, or simply do not appear in,
Microsoft's documentation. Do not re-derive them from the docs.

**Tenant-neutral by design.** Bot IDs, environment GUIDs, site names, drive-item IDs and
workbook names are deliberately absent — record those in the project's own governance, not
here. What lives here is connector behaviour that holds across tenants.

## The Golden Rule

> A pinned File input is an opaque ID, not a filename. Until you have resolved that ID back
> to a filename yourself, you do not know which workbook the tool writes to — and a
> wrongly-pinned tool is more dangerous than an unpinned one, because it fails identically
> every time and, against a healthy workbook, would not fail at all.

## Verification context

Findings were established with Standard orchestration (reached via "Other ways to build", not
the GitHub Copilot harness), a draft unpublished agent, and end-user credentials on the
connection. A purpose-built smoke-test workbook with one formatted table and a unique key
column was used as the control — **build one before trusting any finding against a client
file.** A control workbook is what separates "the connector is broken" from "this workbook is
broken."

## Operation coverage — what actually works

| Operation | Connector action | Status |
|---|---|---|
| Read | `List rows present in a table` (`GetItems`) | Works. ~5s observed. |
| Create | `Add a row into a table` (`AddRowV2`) | Works. ~80s observed. |
| Update | `Update a row` (`PatchItem`) | Works. Verified at XML level. ≤20s observed. |
| Delete | — | **No connector action exists.** Requires an Office Script via `Run script`. |

Copilot Studio exposes **13** Excel Online (Business) actions; none deletes a row, though the
underlying connector REST API supports it. Searching "delete" inside the connector returns
nothing.

Delete requires a script authored **inside Excel on the web** (Automate → New Script). A
hand-built `.osts` uploaded to the library has never been shown to bind — do not assume it does.

**Latencies above are single observations, not benchmarks.** Treat them as order-of-magnitude
only; they are recorded to set expectations about slowness, not to budget a workflow.

## Pre-Flight Checklist — gates any write tool pointed at client data

No item is skipped silently. Each maps to a named failure mode below.

1. **File pin resolved to a filename?** Take the pinned drive-item ID, resolve it against the
   library listing, and read the `name` back. Yes/no. → *Finding 1.*
2. **All five write inputs pinned?** Location · Document Library · File · Table · Key Column.
   None left on "Dynamically fill with AI". Yes/no. → *Finding 1.*
3. **Key column uniqueness confirmed?** Read the whole table and check the key column for
   duplicates. If uniqueness is not guaranteed, an Update is a coin flip — route through an
   Office Script that handles all matches, or do not wire the tool. Yes/no. → *Finding 5.*
4. **Update confirmation gate decided?** `Update a row` executes with no prompt by default.
   Decide explicitly whether it stays ungated before the tool touches client data — do not
   inherit the default by omission. Yes/no. → *Finding 4.*
5. **Verification method cache-busted?** The read used to confirm the write must carry
   `cache: 'no-store'` **and** a cache-busting query parameter. Yes/no. → *Finding 3.*

**Validate after every write:** re-read the stored `.xlsx` and cite what the file actually
contains. Never cite the agent's own confirmation text — see *Finding 2*.

## Finding 1 — Pin all five write targets, then verify the File pin resolves to a filename

`Add a row` and `Update a row` ship with **Location, Document Library, and File** all set to
"Dynamically fill with AI". Pin all three, plus **Table** and **Key Column**.

**Pinning is not sufficient.** The pin stores an opaque 34-character drive-item ID with no
filename shown anywhere in the UI. Observed live: an `Update a row` tool was found pinned to a
*different workbook in the same library* than intended. Every update returned `502 BadGateway`
— which read exactly like "Copilot Studio cannot update rows" and nearly became a permanently
recorded limitation.

A wrongly-pinned target is worse than an unpinned one: it fails identically every time, and
against a healthy workbook it would not error at all — it would silently write to the wrong
client's file.

**The check — resolve the ID back to a name:**

```
GET https://{tenant}.sharepoint.com/sites/{site}/_api/v2.0/drives
GET https://{tenant}.sharepoint.com/sites/{site}/_api/v2.0/drives/{driveId}/root/children
```

Match `id` → `name`. Run this after pinning or changing any File input, before trusting the tool.

## Finding 2 — Never trust the agent's success message; re-read the stored file

Two narration failures were observed, **both with a correct underlying write**:

- On a duplicate key, the agent reported the before-value of the *other* duplicate row.
- On an oblique phrasing it emitted a refusal ("no information available confirming it has
  moved") and then performed the update anyway, in the same turn.

The writes were correct; the prose describing them was not. **Any workflow that logs or
forwards the agent's confirmation text is logging fiction.** Log the re-read, not the narration.

## Verification procedure

Fetch the stored `.xlsx` and inspect its XML. Do not rely on the agent, the Excel web UI's
rendered view, or an uncached SharePoint read.

**Cache-busting fetch — both parts are required:**

```js
const url = `https://{tenant}.sharepoint.com/sites/{site}` +
  `/_api/web/GetFileByServerRelativeUrl('${serverRelativePath}')/$value` +
  `?cb=${Date.now()}`;                     // cache-busting query parameter
const res = await fetch(url, {
  cache: 'no-store',                       // and no-store
  headers: { Accept: 'application/octet-stream' },
});
```

**XML paths to check inside the unzipped `.xlsx`:**

| Path | What it tells you |
|---|---|
| `xl/tables/table1.xml` | The table `ref` — did the range grow on an Add? |
| `xl/worksheets/sheet1.xml` | Cell positions and value/type references |
| `xl/sharedStrings.xml` | The actual string values the cells point at |

**Corroborating metadata** (useful, but not a substitute for reading the bytes): `Length`,
`UIVersionLabel`, `TimeLastModified`, `ETag`.

## Finding 3 — Cache-bust every verification read

`/_api/web/GetFileByServerRelativeUrl('...')/$value` **serves stale bytes.** Observed live: it
reported a file as byte-identical after a successful write, while the metadata (`Length`,
`UIVersionLabel`, `TimeLastModified`, `ETag`) was already showing the new version.

Without both `cache: 'no-store'` **and** a cache-busting query parameter, verification
produces confident false negatives — the most expensive failure mode in this area, because it
makes working code look broken and invites a permanent "limitation" that does not exist.

## Finding 4 — Confirmation gates are asymmetric, and backwards

`Add a row` prompts *"I am about to add a new row… Confirm to proceed?"*. `Update a row` does
**not** — it executes immediately. **The additive operation is gated; the destructive one is not.**

Treat the current asymmetry as a defect to correct, not a default to inherit. Decide the
Update gate explicitly (Pre-Flight item 4) before any tool is pointed at client data.

**Config trap:** setting *"Ask the end user before running: Yes"* saves successfully with an
empty message field, then fails **every** invocation at runtime with `InvalidContent`. The
message field only appears after toggling Yes — so it is easy to save a gate that bricks the tool.

## Finding 5 — Duplicate keys are silently permitted; Update hits the first match only

`Add a row` will create a second row with an existing key — no warning, no error. `Update a
row` then modifies **only the first match**, silently. Verified directly: two rows sharing a
key, update by that key, only the earlier row changed.

For any table where the key column is not guaranteed unique, an update is a coin flip. Confirm
uniqueness before wiring an Update tool, or route the operation through an Office Script that
handles all matches explicitly.

## Finding 6 — Batch work does not belong on this connector

Writes are per-row and slow. Anything batch-shaped — bulk reconciliation, multi-row status
sweeps, column-wide rewrites — needs **Office Scripts**, not a loop over `Add a row` /
`Update a row`.

## Finding 7 — A workbook can be intrinsically defective, and it is not a connector limit

A single workbook can return `502 BadGateway` on **every** workbook-level operation while other
files in the same library work normally — including against a freshly uploaded clean copy of
the same file. SharePoint deterministically reprocesses such a file on upload and quarantines
unparseable parts into `[trash]` entries; that is a **symptom, not the cause**.

Hypotheses worth eliminating early, all of which have been ruled out at least once: strict Open
XML · `[trash]` parts · absence of a formatted table · stale upload · the generating library
(a working control file produced by the same `openpyxl` version behaved fine).

**This class of defect reproduces identically in Power Automate, Office Scripts, and the Graph
API.** It is not a Copilot Studio limitation. Regenerating the workbook is the only known path.
The diagnostic that separates it from Finding 1: a wrong File pin also yields uniform 502s —
resolve the pin to a filename *first*, then suspect the workbook.

## Behaviour that held up (verified, not assumed)

Tool selection was correct on **6/6** varied phrasings, including two that required inference
from natural language to the right column and value — e.g. a retire-this-asset phrasing
resolving to the `Status` column, and a colloquial "it moved offices" phrasing resolving both
the row identity and the `Location` column.

A key matching nothing fails cleanly: HTTP `404`, `No row was found with Id '<key>'`.

⚠ **The raw connector error is surfaced verbatim to the end user.** Acceptable in testing; not
acceptable in a client-facing deployment — wrap or suppress it before anyone outside the build
sees the agent.

## Environment gotchas

- **Backgrounded Chrome tabs render nothing.** `document.visibilityState === "hidden"` means
  blank screenshots and an unrendered SPA. A prior session recorded "Excel on the web never
  renders" and "Copilot Studio breaks on deep links" as platform facts; **both were this.**
  Foreground the tab before concluding a page is broken.
- **An empty dynamic dropdown is a downstream symptom, not a UI bug.** Table and Key Column
  return "Could not find suggestions" *because the File pin above them is wrong*. Once the
  File is corrected, Table resolves normally. Treat an empty dropdown as a signal to check the
  pin above it — never work around it with "Enter custom value".
- **Table accepts either the table name or its braced GUID.** Binding is by GUID, so it survives
  a rename but breaks if the table is deleted and recreated.
- **Copilot Studio blocks navigation with a "Leave the page?" dialog** on a stale dirty flag,
  even after a successful save.
- **The test panel caches the agent version** — start a "New test session" after any config
  change, or you are testing the previous build.

## Two Strikes

Same operation fails or returns wrong twice → **STOP.** Research, append to Known Issues,
report. No blind third retry. In this domain the two most likely causes of a repeated failure
are a wrong File pin (Finding 1) and a stale verification read (Finding 3) — check both before
concluding the connector cannot do the thing.

## Integration with other skills

| Load alongside | When |
|---|---|
| `ms365-governance` | Any SharePoint read/write, tenant scope, or access-state question. |
| The project's own workbook/tracker governance | The target workbook is a governed client file — that skill owns its schema, write rules, and any known defects. |
| `five-whys` | A repeated failure here — the 502 incident is the worked example of misattributing a caller bug to a platform limit. |

## Known Issues

| Date | Issue | Resolution |
|------|-------|------------|
| 2026-08-14 | **Finding 1** — File pin stores an opaque 34-char drive-item ID with no filename in the UI; an `Update a row` tool was found pinned to the wrong workbook in the same library, producing a uniform `502 BadGateway` that read as a connector limitation. | Resolve every File pin ID → filename via `_api/v2.0/drives/{driveId}/root/children`. Pre-Flight item 1. |
| 2026-08-14 | **Finding 2** — Agent narration misreports writes: reported the before-value of the wrong duplicate row; emitted a refusal then performed the update in the same turn. Underlying writes were correct. | Never log or forward the agent's confirmation text. Verify by re-reading the stored `.xlsx`. |
| 2026-08-14 | **Finding 3** — `GetFileByServerRelativeUrl(...)/$value` serves stale bytes; reported a file byte-identical after a successful write while metadata showed the new version. | Every verification fetch needs `cache: 'no-store'` **and** a cache-busting query parameter. |
| 2026-08-14 | **Finding 4** — Confirmation gates asymmetric and backwards: `Add a row` prompts, `Update a row` executes immediately. Separately, "Ask the end user before running: Yes" saves with an empty message field and then fails every invocation with `InvalidContent`. | Decide the Update gate explicitly per deployment. Never save the ask-first toggle without filling the message field. |
| 2026-08-14 | **Finding 5** — Duplicate keys silently permitted by `Add a row`; `Update a row` modifies only the first match, silently. | Confirm key-column uniqueness before wiring an Update tool, or route through an Office Script handling all matches. |
| 2026-08-14 | **Finding 6** — No delete action exists among the 13 exposed Excel Online (Business) actions, though the connector REST API supports it. | Delete (and all batch work) via an Office Script authored inside Excel on the web (Automate → New Script). A hand-built uploaded `.osts` has not been shown to bind. |
| 2026-08-14 | **Finding 7** — A single workbook returned `502 BadGateway` on every workbook-level operation, including a freshly uploaded clean copy, while siblings in the same library worked. Not a Copilot Studio limitation; reproduces in Power Automate, Office Scripts, Graph API. | Rule out the File pin first, then regenerate the workbook. Do not re-investigate the eliminated hypotheses listed in Finding 7. |
| 2026-08-14 | Backgrounded Chrome tabs render nothing (`visibilityState === "hidden"`), previously misrecorded as "Excel on the web never renders" and "Copilot Studio breaks on deep links". | Foreground the tab before concluding a page is broken. |
| 2026-08-14 | Empty Table / Key Column dropdowns ("Could not find suggestions") caused by a wrong upstream File pin, not a UI defect. | Check the pin above the dropdown; do not use "Enter custom value" as a workaround. |
| 2026-08-14 | Raw connector errors (e.g. `No row was found with Id '<key>'`) are surfaced verbatim to the end user. | Acceptable in testing only. Wrap or suppress before any client-facing deployment. |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.