{"record":{"id":"624936deb35ff01b","repo":"Hmbown/CodeWhale","slug":"region-must-be-integer-x-y-width-height-with-positive-size","errorCode":null,"errorMessage":"region must be integer [x,y,width,height] with positive size","messagePattern":"region must be integer \\[x,y,width,height\\] with positive size","errorType":"validation","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/win32.mjs","lineNumber":407,"sourceCode":"    $value = ''; $vp = $null;\n    if (-not $cur.Current.IsPassword -and $cur.TryGetCurrentPattern([System.Windows.Automation.ValuePattern]::Pattern, [ref]$vp)) { $value = [string]$vp.Current.Value }\n    [void]$els.Add([pscustomobject]@{ index = $els.Count; path = @($path); runtime_id = @($cur.GetRuntimeId()); window_runtime_id = $windowId; role = [string]$cur.Current.ControlType.ProgrammaticName; label = [string]$cur.Current.Name; value = $value.Substring(0, [Math]::Min(120, $value.Length)); enabled = $cur.Current.IsEnabled;\n      x = [int]$rect.X; y = [int]$rect.Y; w = [int]$rect.Width; h = [int]$rect.Height; actions = $acts });\n    $kids = $cur.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition);\n    for ($i = $kids.Count - 1; $i -ge 0; $i--) { $stack.Push(@($kids[$i], ($path + $i))) }\n  }\n  break;\n}\n$result = [pscustomobject]@{ found = $found; name = $appName; truncated = $truncated; elements = @($els | ForEach-Object { [pscustomobject]@{ index = $_.index; path = @($_.path); runtime_id = $_.runtime_id; window_runtime_id = $_.window_runtime_id; role = ($_.role -replace 'ControlType.',''); label = $_.label; value = $_.value; enabled = $_.enabled; position = [pscustomobject]@{ x = $_.x; y = $_.y }; size = [pscustomobject]@{ w = $_.w; h = $_.h }; actions = $_.actions } }) };\nWrite-Output ($result | ConvertTo-Json -Depth 6 -Compress);`, { timeoutMs: 60_000 });\n      if (!j.found) throw new ExecError(\"application window not found in UIA tree — pass app_ref.name as the exact window title from list_windows or list_apps.title\");\n      return j;\n    },\n    screenshot: async (args = {}) => {\n      if (Object.hasOwn(args, \"app_ref\") || Object.hasOwn(args, \"window_id\")) throw unsupportedSelector(\"Windows screenshot does not support app_ref or window_id; omit them for a desktop screenshot\");\n      const { display = activeDisplay, region, path: outPath } = args;\n      if (display != null && (!Number.isInteger(display) || display < 1)) throw new ExecError(\"display index must be a positive integer\");\n      if (region != null && (!Array.isArray(region) || region.length !== 4 || !region.every(Number.isInteger) || region[2] <= 0 || region[3] <= 0)) throw new ExecError(\"region must be integer [x,y,width,height] with positive size\");\n      const dir = recordingsDir();\n      fs.mkdirSync(dir, { recursive: true });\n      const file = path.resolve(outPath || path.join(dir, `shot-${crypto.randomBytes(6).toString(\"hex\")}.png`));\n      const meta = await psJson(`Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing;\n$bounds = [System.Windows.Forms.SystemInformation]::VirtualScreen;\n${display == null ? \"\" : `$screens = [System.Windows.Forms.Screen]::AllScreens; if (${display} -gt $screens.Count) { throw 'display index is out of range' }; $bounds = $screens[${display - 1}].Bounds;`}\n${region == null ? \"\" : `$crop = New-Object System.Drawing.Rectangle(${region.join(\",\")}); if (-not $bounds.Contains($crop)) { throw 'region is outside capture bounds' }; $bounds = $crop;`}\n$bmp = New-Object System.Drawing.Bitmap($bounds.Width, $bounds.Height);\ntry {\n  $g = [System.Drawing.Graphics]::FromImage($bmp);\n  try { $g.CopyFromScreen($bounds.X, $bounds.Y, 0, 0, $bounds.Size); } finally { $g.Dispose(); }\n  $bmp.Save('${file.replace(/'/g, \"''\")}', [System.Drawing.Imaging.ImageFormat]::Png);\n} finally { $bmp.Dispose(); }\n@{ x = $bounds.X; y = $bounds.Y; w = $bounds.Width; h = $bounds.Height } | ConvertTo-Json -Compress;`, { timeoutMs: 30_000 });\n      if (!fs.existsSync(file) || ![meta.x, meta.y, meta.w, meta.h].every(Number.isFinite) || meta.w <= 0 || meta.h <= 0) throw new ExecError(\"screenshot did not return a valid raster\");\n      lastRaster = { file, bytes: fs.statSync(file).size, points: { x: meta.x, y: meta.y, w: meta.w, h: meta.h }, pixels: { w: meta.w, h: meta.h }, scale: 1, capturedAt: new Date().toISOString() };\n      return { ...lastRaster };\n    },","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/win32.mjs#L389-L425","documentation":"screenshot validates the region argument: it must be an array of exactly 4 integers [x,y,width,height] with positive width and height. It throws this ExecError otherwise, since the region is interpolated directly into a System.Drawing.Rectangle in the PowerShell capture script.","triggerScenarios":"Passing region as {x,y,w,h} object instead of array; floats like [10.5, 0, 100, 100]; negative or zero width/height; fewer/more than 4 elements; null when a region was expected.","commonSituations":"Porting from APIs that take rect objects; computed coordinates producing NaN or floats from scaling; an agent emitting [x1,y1,x2,y2] corner format instead of [x,y,w,h].","solutions":["Pass exactly [x, y, width, height] as integers with width > 0 and height > 0","Convert corner-format rects: [x1, y1, x2-x1, y2-y1]","Round or Math.trunc computed coordinates to integers before calling","Validate array length and integrality on the caller side"],"exampleFix":"// before\nawait screenshot({ region: { x: 0, y: 0, w: 800, h: 600 } });\n// after\nawait screenshot({ region: [0, 0, 800, 600] });","handlingStrategy":"validation","validationCode":"function toRegionInts(rect) {\n  const [x1, y1, x2, y2] = Array.isArray(rect) && rect.length === 4\n    ? rect\n    : [rect.x, rect.y, rect.x + rect.w, rect.y + rect.h];\n  const region = [x1, y1, x2 - x1, y2 - y1].map(Math.round);\n  if (region.length !== 4 || region.some(n => !Number.isInteger(n)) || region[2] <= 0 || region[3] <= 0) {\n    throw new TypeError(\"region must resolve to integer [x,y,width,height] with positive size\");\n  }\n  return region;\n}","typeGuard":"const isValidRegion = (r) => Array.isArray(r) && r.length === 4\n  && r.every(Number.isInteger) && r[2] > 0 && r[3] > 0;","tryCatchPattern":"try {\n  return await backend.screenshot({ region });\n} catch (e) {\n  if (String(e.message).startsWith(\"region must be integer\")) {\n    return await backend.screenshot(); // full-screen fallback\n  }\n  throw e;\n}","preventionTips":["Always pass [x, y, width, height], not rect objects or corner pairs","Round computed coordinates to integers","Reject zero/negative width or height before calling","Clamp regions to the display bounds"],"tags":["screenshot","validation","geometry"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}