iOfficeAI/OfficeCLI · error · ArgumentException
Invalid srcRect '{compound}'. Expected 'l=10,r=10,t=5,b=5' (
Error message
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. What it means
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.
Source
Thrown at src/officecli/Handlers/Excel/ExcelHandler.Helpers.Drawing.cs:113
int? l = null, r = null, t = null, b = null;
if (properties.TryGetValue("srcRect", out var compound) && !string.IsNullOrWhiteSpace(compound))
{
// Track whether any piece parsed so we can throw a clear error
// instead of silently no-oping (which would also wipe existing
// srcRect because the caller replaces with ParseSrcRect's null).
bool anyParsed = false;
foreach (var piece in compound.Split(',', StringSplitOptions.RemoveEmptyEntries))
{
var kv = piece.Split('=', 2);
if (kv.Length != 2) continue;
var key = kv[0].Trim().ToLowerInvariant();
var val = ParseCropPercent(kv[1]);
if (!val.HasValue) continue;
switch (key) { case "l": l = val; break; case "r": r = val; break; case "t": t = val; break; case "b": b = val; break; }
anyParsed = true;
}
if (!anyParsed)
throw new ArgumentException(
$"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.");
}
// CONSISTENCY(picture-crop): bare composite `crop=l,t,r,b` — the exact
// form Get emits (and pptx Add already accepts). Without it, dump→batch
// replay warned UNSUPPORTED and silently dropped the srcRect.
if (properties.TryGetValue("crop", out var cropAll) && !string.IsNullOrWhiteSpace(cropAll)
&& !cropAll.Contains('='))
{
var cropParts = cropAll.Split(',');
var cropVals = cropParts.Length == 4
? cropParts.Select(ParseCropPercent).ToArray()
: null;
if (cropVals == null || !cropVals.All(v => v.HasValue))
throw new ArgumentException(
$"Invalid crop '{cropAll}'. Expected four comma-separated percentages in l,t,r,b order (e.g. '10,15,5,20').");
l = cropVals[0]; t = cropVals[1]; r = cropVals[2]; b = cropVals[3];
}View on GitHub (pinned to 1ced45e900)
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.
Example fix
// before props["srcRect"] = "10,15,5,20"; // numbers, no keys -> throw props["srcRect"] = "left=10,right=10"; // wrong keys -> throw // after props["srcRect"] = "l=10,r=10,t=5,b=5"; // key form // or, for the same ordered numbers: props["crop"] = "10,15,5,20";
Defensive patterns
Strategy: validation
Validate before calling
static bool IsValidSrcRect(string s)
{
bool any = false;
foreach (var piece in s.Split(',', StringSplitOptions.RemoveEmptyEntries))
{
var kv = piece.Split('=', 2);
if (kv.Length != 2) continue;
var key = kv[0].Trim().ToLowerInvariant();
if (key is not ("l" or "r" or "t" or "b")) continue;
if (!int.TryParse(kv[1].Trim(), out var v) || v < 0 || v > 100) continue;
any = true;
}
return any;
}
if (!string.IsNullOrWhiteSpace(srcRect) && !IsValidSrcRect(srcRect))
throw new ArgumentException($"Bad srcRect '{srcRect}'; use l/r/t/b=<0-100> pairs"); Type guard
static bool IsValidSrcRect(string s) =>
s.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split('=', 2))
.Where(kv => kv.Length == 2)
.Any(kv => (kv[0].Trim().ToLowerInvariant() is "l" or "r" or "t" or "b")
&& int.TryParse(kv[1].Trim(), out var v) && v is >= 0 and <= 100); Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Invalid crop '{cropAll}'. Expected four comma-separated perc
- Picture/shape {key} is out of range for a oneCell/absolute d
- Sheet not found: {shpSheetName}
- Defined name '{nrName}' collides with the table name '{exist
- Property 'sqref' (or 'range'/'ref') is required for validati
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/e1edbf0b0901117d.
Report an issue: GitHub.