{"record":{"id":"7f503a6497c6fb26","repo":"iOfficeAI/OfficeCLI","slug":"a-sheet-named-casematch-name-already-exists-s","errorCode":null,"errorMessage":"A sheet named '{caseMatch.Name}' already exists. Sheet names must be unique.","messagePattern":"A sheet named '(.+?)' already exists\\. Sheet names must be unique\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs","lineNumber":72,"sourceCode":"            // user-created sheet (collision is a genuine error). The\n            // placeholder is identifiable as: workbook holds exactly one\n            // sheet, that sheet's worksheet has empty SheetData, no\n            // sheetView properties beyond defaults, no tabColor — i.e.\n            // a fresh `Create blank → first Add` flow.\n            var caseExact = string.Equals(caseMatch.Name, name, StringComparison.Ordinal);\n            var isPlaceholder = sheets.Elements<Sheet>().Count() == 1\n                && IsPristineWorksheet(workbookPart, caseMatch);\n            // Placeholder claim is only meaningful when the caller actually\n            // supplies a sheet-level prop that would mutate the placeholder\n            // (autoFilter / tabColor / hidden). Without any such prop the\n            // \"claim\" is a true no-op and indistinguishable from a duplicate-\n            // name collision — reject so callers don't see fake success.\n            var hasClaimableProp = properties.ContainsKey(\"autoFilter\")\n                || properties.ContainsKey(\"tabColor\")\n                || properties.ContainsKey(\"hidden\");\n            if (!caseExact || !isPlaceholder || !hasClaimableProp)\n            {\n                throw new ArgumentException(\n                    $\"A sheet named '{caseMatch.Name}' already exists. Sheet names must be unique.\");\n            }\n\n            // Placeholder claim: route any supplied autoFilter / tabColor /\n            // hidden through Set so the user's intent applies — the previous\n            // silent no-op branch dropped them, which is what motivated\n            // rejecting duplicates outright.\n            var existingPart = (WorksheetPart)workbookPart.GetPartById(caseMatch.Id!);\n            var sheetMerged = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);\n            if (properties.TryGetValue(\"autoFilter\", out var dupAf)) sheetMerged[\"autofilter\"] = dupAf;\n            if (properties.TryGetValue(\"tabColor\", out var dupTc)) sheetMerged[\"tabcolor\"] = dupTc;\n            if (sheetMerged.Count > 0)\n                SetSheetLevel(existingPart, name, sheetMerged);\n            if (properties.TryGetValue(\"hidden\", out var dupHidden) && ParseHelpers.IsTruthy(dupHidden))\n                caseMatch.State = SheetStateValues.Hidden;\n            return $\"/sheet[@name='{name}']\";\n        }\n        var newWorksheetPart = workbookPart.AddNewPart<WorksheetPart>();","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs#L54-L90","documentation":"Thrown by ExcelHandler.AddSheet when a sheet with the requested name already exists (case-insensitive match) and none of the exception conditions apply (ifExists=use is not set, the existing sheet is not the pristine placeholder, or no claimable property was supplied). Excel requires sheet names to be unique (case-insensitive), so adding a second sheet with the same name is a hard error. The guard distinguishes a genuine duplicate-name collision from a legitimate placeholder-claim flow.","triggerScenarios":"Calling Add with type=sheet and a name that matches an existing sheet, without setting ifExists=use and without targeting the BlankDocCreator placeholder. For example: workbook already has 'Sheet1' and you Add another sheet named 'Sheet1'. Or: Add with name='Data' when 'Data' already exists with content.","commonSituations":"A batch replay that creates sheets which already exist in the target workbook (common when dumping from one workbook and replaying onto another that already has those sheets); a script that auto-generates sheet names without checking for collisions; a user adding a sheet whose name differs only in case from an existing one ('Data' vs 'DATA').","solutions":["Set ifExists=use in the properties to claim the existing sheet as a no-op success (this is what single-sheet subtree dumps emit for replay compatibility).","Choose a unique sheet name.","Delete or rename the existing sheet first before adding the new one.","If replaying a dump onto a workbook that already has the sheet, ensure the batch items include ifExists=use on the 'add sheet' command."],"exampleFix":"// before: adding a sheet whose name already exists\nhandler.Add(\"/\", \"sheet\", new { name = \"Sheet1\" }); // throws if Sheet1 exists\n\n// after: claim the existing sheet as a no-op\nhandler.Add(\"/\", \"sheet\", new { name = \"Sheet1\", ifExists = \"use\" });","handlingStrategy":"validation","validationCode":"// Check for existing sheet name before adding\nvar existing = handler.GetSheetNames();\nif (existing.Any(s => s.Equals(name, StringComparison.OrdinalIgnoreCase)))\n{\n    // Use ifExists=use to claim the existing sheet, or pick a different name\n    handler.Add(\"/\", \"sheet\", new { name, ifExists = \"use\" });\n}\nelse\n{\n    handler.Add(\"/\", \"sheet\", new { name });\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    handler.Add(\"/\", \"sheet\", new { name = sheetName });\n}\ncatch (ArgumentException ex) when (ex.Message.Contains(\"already exists\"))\n{\n    // Sheet exists — claim it as a no-op for replay-safe batch operations\n    handler.Add(\"/\", \"sheet\", new { name = sheetName, ifExists = \"use\" });\n}","preventionTips":["Use ifExists=use when replaying dumps onto workbooks that may already contain the target sheet.","Check existing sheet names before adding to choose a unique name.","In batch operations, always include ifExists=use on add-sheet commands for replay safety.","Remember Excel sheet names are case-insensitive — 'Data' and 'DATA' collide."],"tags":["excel","add-sheet","duplicate-name","uniqueness-constraint","batch-replay"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}