{"record":{"id":"a28663fa48ded3d4","repo":"iOfficeAI/OfficeCLI","slug":"invalid-crop-cropall-expected-four-comma-sepa","errorCode":null,"errorMessage":"Invalid crop '{cropAll}'. Expected four comma-separated percentages in l,t,r,b order (e.g. '10,15,5,20').","messagePattern":"Invalid crop '(.+?)'\\. Expected four comma-separated percentages in l,t,r,b order \\(e\\.g\\. '10,15,5,20'\\)\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Helpers.Drawing.cs","lineNumber":128,"sourceCode":"                anyParsed = true;\n            }\n            if (!anyParsed)\n                throw new ArgumentException(\n                    $\"Invalid srcRect '{compound}'. Expected 'l=10,r=10,t=5,b=5' (any subset; values are percent 0-100). \"\n                    + \"For raw l/t/r/b numbers use cropLeft/cropTop/cropRight/cropBottom keys.\");\n        }\n        // CONSISTENCY(picture-crop): bare composite `crop=l,t,r,b` — the exact\n        // form Get emits (and pptx Add already accepts). Without it, dump→batch\n        // replay warned UNSUPPORTED and silently dropped the srcRect.\n        if (properties.TryGetValue(\"crop\", out var cropAll) && !string.IsNullOrWhiteSpace(cropAll)\n            && !cropAll.Contains('='))\n        {\n            var cropParts = cropAll.Split(',');\n            var cropVals = cropParts.Length == 4\n                ? cropParts.Select(ParseCropPercent).ToArray()\n                : null;\n            if (cropVals == null || !cropVals.All(v => v.HasValue))\n                throw new ArgumentException(\n                    $\"Invalid crop '{cropAll}'. Expected four comma-separated percentages in l,t,r,b order (e.g. '10,15,5,20').\");\n            l = cropVals[0]; t = cropVals[1]; r = cropVals[2]; b = cropVals[3];\n        }\n        foreach (var (key, fld) in new[] { (\"crop.l\", \"l\"), (\"crop.r\", \"r\"), (\"crop.t\", \"t\"), (\"crop.b\", \"b\") })\n        {\n            if (properties.TryGetValue(key, out var vs) && !string.IsNullOrWhiteSpace(vs))\n            {\n                var v = ParseCropPercent(vs);\n                if (!v.HasValue) continue;\n                switch (fld) { case \"l\": l = v; break; case \"r\": r = v; break; case \"t\": t = v; break; case \"b\": b = v; break; }\n            }\n        }\n        // CONSISTENCY(picture-crop): Office-API-style `cropLeft`/`cropRight`\n        // /`cropTop`/`cropBottom` aliases. Accept fraction (<=1 → *100%) or\n        // percent (>1 → as-is); e.g. `cropLeft=0.1` and `cropLeft=10` both\n        // mean 10% crop from left.\n        foreach (var (key, fld) in new[] { (\"cropLeft\", \"l\"), (\"cropRight\", \"r\"), (\"cropTop\", \"t\"), (\"cropBottom\", \"b\") })\n        {","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Helpers.Drawing.cs#L110-L146","documentation":"ParseSrcRect's 'crop' composite branch accepts 'l,t,r,b' as exactly four comma-separated percentages (the exact form Get emits, supported so dump->batch replay works). If there are not exactly four parts or any part fails ParseCropPercent, it throws. Order is left, top, right, bottom. This branch only runs when crop has no '=' (the key form routes to srcRect).","triggerScenarios":"crop=10,15,5 (only three); crop=10,abc,5,20 (non-numeric); crop=10;15;5;20 (semicolon separator); crop=10,15,5,20,1 (five values).","commonSituations":"Truncated crop string from a manual edit; locale using ';' as list separator; wrong element count or wrong order.","solutions":["Provide exactly four percentages in l,t,r,b order: crop=10,15,5,20.","For partial crops use the per-side keys crop.l/crop.t/crop.r/crop.b.","Round-trip via Get to obtain the canonical crop string and replay it verbatim.","Validate the part count and numeric parse before calling Add."],"exampleFix":"// before\nprops[\"crop\"] = \"10,15,5\";      // only three -> throw\nprops[\"crop\"] = \"10;15;5;20\";   // wrong separator -> throw\n\n// after\nprops[\"crop\"] = \"10,15,5,20\";   // exactly four, l,t,r,b\n// or partial:\nprops[\"crop.l\"] = \"10\";\nprops[\"crop.r\"] = \"5\";","handlingStrategy":"validation","validationCode":"static bool IsValidCropComposite(string s)\n{\n    var parts = s.Split(',');\n    return parts.Length == 4\n        && parts.All(p => int.TryParse(p.Trim(), out var v) && v >= 0 && v <= 100);\n}\n\nif (!string.IsNullOrWhiteSpace(crop) && !crop.Contains('=') && !IsValidCropComposite(crop))\n    throw new ArgumentException($\"Bad crop '{crop}'; expected four l,t,r,b percentages\");","typeGuard":"static bool IsValidCropComposite(string s) =>\n    s.Split(',') is { Length: 4 } parts\n    && parts.All(p => int.TryParse(p.Trim(), out var v) && v is >= 0 and <= 100);","tryCatchPattern":null,"preventionTips":["Provide exactly four comma-separated percentages in l,t,r,b order.","Use crop.l/crop.t/crop.r/crop.b keys for partial crops.","Use the srcRect key form for explicit l/r/t/b assignment.","Replay the exact crop string Get emits to avoid reformatting mistakes."],"tags":["excel","ooxml","drawing","crop","validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}