{"record":{"id":"e1edbf0b0901117d","repo":"iOfficeAI/OfficeCLI","slug":"invalid-srcrect-compound-expected-l-10-r-10","errorCode":null,"errorMessage":"Invalid srcRect '{compound}'. Expected 'l=10,r=10,t=5,b=5' (any subset; values are percent 0-100). For raw l/t/r/b numbers use cropLeft/cropTop/cropRight/cropBottom keys.","messagePattern":"Invalid srcRect '(.+?)'\\. Expected 'l=10,r=10,t=5,b=5' \\(any subset; values are percent 0-100\\)\\. For raw l/t/r/b numbers use cropLeft/cropTop/cropRight/cropBottom keys\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Helpers.Drawing.cs","lineNumber":113,"sourceCode":"        int? l = null, r = null, t = null, b = null;\n        if (properties.TryGetValue(\"srcRect\", out var compound) && !string.IsNullOrWhiteSpace(compound))\n        {\n            // Track whether any piece parsed so we can throw a clear error\n            // instead of silently no-oping (which would also wipe existing\n            // srcRect because the caller replaces with ParseSrcRect's null).\n            bool anyParsed = false;\n            foreach (var piece in compound.Split(',', StringSplitOptions.RemoveEmptyEntries))\n            {\n                var kv = piece.Split('=', 2);\n                if (kv.Length != 2) continue;\n                var key = kv[0].Trim().ToLowerInvariant();\n                var val = ParseCropPercent(kv[1]);\n                if (!val.HasValue) continue;\n                switch (key) { case \"l\": l = val; break; case \"r\": r = val; break; case \"t\": t = val; break; case \"b\": b = val; break; }\n                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        }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Helpers.Drawing.cs#L95-L131","documentation":"ParseSrcRect parses the 'srcRect' property as comma-separated key=value pairs where keys are exactly l/r/t/b and values are percentages 0-100. If NONE of the pieces parse to a recognized key with a valid value, it throws rather than silently returning null (which would wipe an existing srcRect because the caller replaces on null). For raw ordered numbers use the 'crop=l,t,r,b' composite or the cropLeft/cropTop/cropRight/cropBottom keys instead.","triggerScenarios":"srcRect=left=10,right=10 (wrong keys -- must be l/r/t/b); srcRect=10,15,5,20 (numbers without keys -- that form belongs to 'crop'); srcRect=abc; srcRect=l=10;right=5 (mixed valid/invalid where none parse).","commonSituations":"Confusing the srcRect key form with the crop ordered form; using full-word keys 'left/top/right/bottom'; pasting a raw numeric crop into srcRect.","solutions":["Use srcRect=l=10,r=10,t=5,b=5 (any subset of l/r/t/b is fine).","For ordered numbers, use crop=10,15,5,20 instead.","For per-side control use crop.l/crop.t/crop.r/crop.b keys.","Validate that at least one l/r/t/b key parses before calling."],"exampleFix":"// before\nprops[\"srcRect\"] = \"10,15,5,20\";     // numbers, no keys -> throw\nprops[\"srcRect\"] = \"left=10,right=10\"; // wrong keys -> throw\n\n// after\nprops[\"srcRect\"] = \"l=10,r=10,t=5,b=5\"; // key form\n// or, for the same ordered numbers:\nprops[\"crop\"] = \"10,15,5,20\";","handlingStrategy":"validation","validationCode":"static bool IsValidSrcRect(string s)\n{\n    bool any = false;\n    foreach (var piece in s.Split(',', StringSplitOptions.RemoveEmptyEntries))\n    {\n        var kv = piece.Split('=', 2);\n        if (kv.Length != 2) continue;\n        var key = kv[0].Trim().ToLowerInvariant();\n        if (key is not (\"l\" or \"r\" or \"t\" or \"b\")) continue;\n        if (!int.TryParse(kv[1].Trim(), out var v) || v < 0 || v > 100) continue;\n        any = true;\n    }\n    return any;\n}\n\nif (!string.IsNullOrWhiteSpace(srcRect) && !IsValidSrcRect(srcRect))\n    throw new ArgumentException($\"Bad srcRect '{srcRect}'; use l/r/t/b=<0-100> pairs\");","typeGuard":"static bool IsValidSrcRect(string s) =>\n    s.Split(',', StringSplitOptions.RemoveEmptyEntries)\n     .Select(p => p.Split('=', 2))\n     .Where(kv => kv.Length == 2)\n     .Any(kv => (kv[0].Trim().ToLowerInvariant() is \"l\" or \"r\" or \"t\" or \"b\")\n                && int.TryParse(kv[1].Trim(), out var v) && v is >= 0 and <= 100);","tryCatchPattern":null,"preventionTips":["Use the key form srcRect=l=10,r=10,t=5,b=5 with short keys l/r/t/b.","Use crop=l,t,r,b (ordered) or cropLeft/cropTop/cropRight/cropBottom for numbers.","Do not mix full-word keys (left/top/right/bottom) -- they will not parse.","Round-trip via Get for the canonical srcRect string before replay."],"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"}