{"record":{"id":"b735d78c312fc4dd","repo":"iOfficeAI/OfficeCLI","slug":"pivot-name-explicitname-already-exists-in-work","errorCode":null,"errorMessage":"Pivot name '{explicitName}' already exists in workbook","messagePattern":"Pivot name '(.+?)' already exists in workbook","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/PivotTableHelper.cs","lineNumber":1263,"sourceCode":"        }\n\n        // 5. Create PivotTablePart at worksheet level\n        pivotPart = targetSheet.AddNewPart<PivotTablePart>();\n        // Link pivot table to cache definition\n        pivotPart.AddPart(cachePart);\n\n        string pivotName;\n        if (properties.TryGetValue(\"name\", out var explicitName) && !string.IsNullOrEmpty(explicitName))\n        {\n            // R8-4 / R8-5 / R11-4 / R16-2: delegate all name validation to\n            // ValidatePivotName so Add and Set share identical rules.\n            explicitName = ValidatePivotName(explicitName);\n            // R6-1: user-supplied name must be unique within the workbook.\n            // Throw ArgumentException rather than silently allowing the\n            // collision (Excel would auto-rename on open, but the on-disk\n            // file would still carry two pivots with the same name).\n            if (existingPivotNames.Contains(explicitName))\n                throw new ArgumentException($\"Pivot name '{explicitName}' already exists in workbook\");\n            pivotName = explicitName;\n        }\n        else\n        {\n            // R6-1: auto-generated default names must also avoid collisions\n            // (two pivots on different sheets otherwise both pick\n            // PivotTable{cacheId+1} with the same cacheId path).\n            pivotName = $\"PivotTable{cacheId + 1}\";\n            int bump = 1;\n            while (existingPivotNames.Contains(pivotName))\n            {\n                bump++;\n                pivotName = $\"PivotTable{cacheId + bump}\";\n            }\n        }\n        var style = properties.GetValueOrDefault(\"style\", \"PivotStyleLight16\");\n\n        // columnNumFmtIds was resolved above (R19-1) and reused here to stamp","sourceCodeStart":1245,"sourceCodeEnd":1281,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/PivotTableHelper.cs#L1245-L1281","documentation":"Thrown when adding a PivotTable whose user-supplied 'name' property already matches an existing pivot name in the workbook. The collision check uses a case-insensitive HashSet of existing pivot names (PivotTableHelper.cs:1116) so 'Sales' and 'SALES' are treated as the same name. R6-1 enforces this because Excel would silently auto-rename on open but leave two pivots with identical names on disk.","triggerScenarios":"Calling add pivot with properties[\"name\"] set to a string already present in the workbook's pivot set. Only fires for the explicit-name branch; auto-generated names (PivotTable{cacheId+1}) fall through to the bump loop at line 1273 and never throw.","commonSituations":"Re-running a script that adds a named pivot without first deleting the prior one; copy-pasting a pivot definition that hardcodes 'PivotTable1'; case-only differences ('Data' vs 'DATA') on case-insensitive filesystems/locales.","solutions":["Pass a unique name, or omit 'name' to let the host auto-generate a collision-free default.","Before adding, query existing pivot names and either delete the old pivot or pick a distinct name.","If scripting idempotent runs, guard with a check: delete the existing pivot of that name first if present."],"exampleFix":"// before\nproperties[\"name\"] = \"SalesPivot\"; // throws if it exists\n\n// after\nif (existingPivotNames.Contains(\"SalesPivot\")) {\n    // delete old pivot, then add\n}\nproperties[\"name\"] = \"SalesPivot\";","handlingStrategy":"validation","validationCode":"// Before adding, gather existing pivot names (case-insensitive) and check.\nvar existing = new HashSet<string>(workbook.PivotTables.Select(p => p.Name), StringComparer.OrdinalIgnoreCase);\nif (existing.Contains(desiredName))\n    throw new InvalidOperationException($\"Pick a unique name; '{desiredName}' is taken.\");","typeGuard":null,"tryCatchPattern":"try { AddPivot(props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"already exists in workbook\"))\n{ /* retry with a suffixed unique name or surface to user */ }","preventionTips":["Centralize pivot-name uniqueness checks in a helper that mirrors the host's case-insensitive HashSet.","Prefer auto-generated names when identity is not semantically meaningful.","In scripted/idempotent flows, delete the old pivot before re-adding by name."],"tags":["pivot-table","validation","argument-exception","excel"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}