iOfficeAI/OfficeCLI · error · ArgumentException
Defined name '{nrName}' already exists in workbook scope; re
Error message
Defined name '{nrName}' already exists in workbook scope; remove it before adding a new one or pick a different name. What it means
Thrown when a defined name with the same identifier AND the same scope (LocalSheetId) already exists in the workbook. OOXML permits the same name across different scopes (workbook-global vs a specific sheet, or two distinct sheets), but a duplicate (name, localSheetId) pair triggers Excel's 'found a problem' repair dialog. The check iterates existing <definedName> entries comparing name (case-insensitive) and LocalSheetId, and additionally checks for collisions with ListObject table names. The message states whether the conflict is sheet-scoped or workbook-scoped.
Source
Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.Tables.cs:191
// recalc engine treats function-flagged defined names as volatile,
// forcing recalc on every workbook change.
if (properties.TryGetValue("volatile", out var nrVolatile) && IsTruthy(nrVolatile))
dn.Function = true;
// CONSISTENCY(definedname-unique): Excel rejects two
// <definedName> entries that share both name AND scope
// (LocalSheetId) with a "found a problem" repair dialog.
// Same name across different scopes (workbook-global vs
// per-sheet, or two distinct sheets) is legal — only the
// (name, localSheetId) pair must be unique.
var dnLocalId = dn.LocalSheetId?.Value;
foreach (var existingDn in definedNames.Elements<DefinedName>())
{
var existingName = existingDn.Name?.Value;
if (existingName == null) continue;
if (!string.Equals(existingName, nrName, StringComparison.OrdinalIgnoreCase)) continue;
if (existingDn.LocalSheetId?.Value == dnLocalId)
throw new ArgumentException(
$"Defined name '{nrName}' already exists" +
(dnLocalId.HasValue ? $" in sheet scope (localSheetId={dnLocalId})" : " in workbook scope") +
"; remove it before adding a new one or pick a different name.");
}
// Mirror of the table-side check: Excel's name namespace spans
// defined names AND ListObject table names; a collision passes
// schema validation but real Excel refuses the file (0x800A03EC).
foreach (var existingTable in _doc.WorkbookPart!.WorksheetParts
.SelectMany(wp => wp.TableDefinitionParts)
.Select(tdp => tdp.Table)
.Where(t => t != null)!)
{
if (string.Equals(existingTable!.Name?.Value, nrName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(existingTable.DisplayName?.Value, nrName, StringComparison.OrdinalIgnoreCase))
throw new ArgumentException(
$"Defined name '{nrName}' collides with the table name '{existingTable.Name?.Value ?? existingTable.DisplayName?.Value}'. Excel requires table and defined names to be unique in one namespace; pick a different name.");
}View on GitHub (pinned to 1ced45e900)
Solutions
- Remove the existing defined name first (via the tool's remove/delete command) then re-add.
- Pick a different name for the new defined range.
- If you want a per-sheet name with the same spelling as a workbook-global one, add it scoped to a specific sheet (different LocalSheetId) — that is legal.
Example fix
// before add ./book.xlsx /namedrange --type namedrange --prop name=SalesTotal --prop ref=Sheet1!A1 // when SalesTotal already exists // after // (option A) remove then re-add, or (option B) rename add ./book.xlsx /namedrange --type namedrange --prop name=SalesTotal2024 --prop ref=Sheet1!A1
Defensive patterns
Strategy: validation
Validate before calling
// Reject (name, scope) duplicates before add, mirroring the handler.
foreach (var dn in definedNames.Elements<DefinedName>())
{
if (string.Equals(dn.Name?.Value, name, StringComparison.OrdinalIgnoreCase)
&& dn.LocalSheetId?.Value == targetLocalSheetId)
throw new InvalidOperationException($"'{name}' already exists in that scope; remove or rename");
}
// Also guard against ListObject table-name collisions. Type guard
static bool DefinedNameIsUnique(IEnumerable<DefinedName> existing, string name, uint? scope)
=> !existing.Any(d => string.Equals(d.Name?.Value, name, StringComparison.OrdinalIgnoreCase)
&& d.LocalSheetId?.Value == scope); Try / catch
try { handler.AddNamedRange(...); }
catch (ArgumentException ex) when (ex.Message.Contains("already exists"))
{ /* remove the old name first, or pick a new name — do NOT retry unchanged */ } Prevention
- Make add-namedrange scripts idempotent: remove-then-add, or check existence first.
- Remember same name in a DIFFERENT scope is legal; only (name, scope) collisions fail.
- Avoid name collisions with ListObject table names — they share Excel's name namespace.
When it happens
Trigger: Calling add namedrange with a `name=` that already exists at the same scope (e.g. re-running a script that adds `SalesTotal` at workbook scope twice); or a name that collides with an existing table name. A same-named name on a DIFFERENT sheet scope does NOT trigger this.
Common situations: Re-running an import script without first removing the previously added name; merging workbooks that each defined the same global name; choosing a name that matches an existing table.
Related errors
- 'name' property is required for namedrange
- Invalid defined-name '{nrName}': must start with a letter/un
- Invalid defined-name '{nrName}': length {nrName.Length} exce
- Invalid defined-name '{nrName}': name parses as a cell refer
- Invalid defined-name '{nrName}': single letter 'R' / 'C' is
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/a33e55be3f4ffdef.
Report an issue: GitHub.