{"record":{"id":"c6ad25e80a2983a6","repo":"aaif-goose/goose","slug":"row-must-be-between-1-and-max-excel-rows","errorCode":null,"errorMessage":"Row must be between 1 and {MAX_EXCEL_ROWS}","messagePattern":"Row must be between 1 and (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-mcp/src/computercontroller/xlsx_tool.rs","lineNumber":232,"sourceCode":"    let parts: Vec<&str> = range.split(':').collect();\n    if parts.len() != 2 {\n        anyhow::bail!(\"Invalid range format. Expected format: 'A1:B10'\");\n    }\n\n    let start = parse_cell_reference(parts[0])?;\n    let end = parse_cell_reference(parts[1])?;\n\n    // parse_cell_reference returns (row, col), so start.0 is row, start.1 is col\n    Ok((start.0, start.1, end.0, end.1))\n}\n\nfn validate_range_bounds(\n    start_row: u32,\n    start_col: u32,\n    end_row: u32,\n    end_col: u32,\n) -> Result<(u32, u32)> {\n    anyhow::ensure!(\n        (1..=MAX_EXCEL_ROWS).contains(&start_row) && (1..=MAX_EXCEL_ROWS).contains(&end_row),\n        \"Row must be between 1 and {MAX_EXCEL_ROWS}\"\n    );\n    anyhow::ensure!(\n        (1..=MAX_EXCEL_COLUMNS).contains(&start_col) && (1..=MAX_EXCEL_COLUMNS).contains(&end_col),\n        \"Column must be between 1 and {MAX_EXCEL_COLUMNS}\"\n    );\n    anyhow::ensure!(\n        start_row <= end_row && start_col <= end_col,\n        \"Range start must not follow range end\"\n    );\n\n    let row_count = end_row\n        .checked_sub(start_row)\n        .and_then(|span| span.checked_add(1))\n        .context(\"Row span overflow\")?;\n    let column_count = end_col\n        .checked_sub(start_col)","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-mcp/src/computercontroller/xlsx_tool.rs#L214-L250","documentation":"validate_range_bounds() checks that both row numbers fall in 1..=MAX_EXCEL_ROWS (1_048_576 — Excel's real row limit, defined at xlsx_tool.rs:6). Rows are 1-based, so row 0 from 'A0' is invalid, as is anything above 1048576. The message interpolates the actual limit when formatted.","triggerScenarios":"Ranges like 'A0:A10' (zero row), 'A1:A9999999' (row past the cap), or agents computing end_row as start_row + oversize offset that overflows the limit. Any get_range() call runs this check after parse_range.","commonSituations":"Whole-column requests translated to 'A1:A9999999'; off-by-one bugs producing row 0; LLM-generated ranges hallucinating 'row 2000000'; data imported from tools that use 0-based coordinates.","solutions":["Use 1-based rows within 1..=1048576 (e.g. 'A1:A1048576' at most).","Replace row 0 with 1 when converting 0-based client coordinates.","Clamp requested end rows to 1048576 before calling the tool.","For large sheets, page through ranges in chunks instead of requesting full columns."],"exampleFix":"// before\nlet range = \"A0:A10\"; // row 0 invalid -> 'Row must be between 1 and 1048576'\n\n// after\nlet range = \"A1:A10\";","handlingStrategy":"validation","validationCode":"const MAX_ROWS: u32 = 1_048_576;\nfn rows_in_bounds(range: &str) -> bool {\n    range.split(':').all(|cell| {\n        let row: u32 = cell.trim_start_matches(|c: char| c.is_ascii_alphabetic())\n            .parse().unwrap_or(0);\n        (1..=MAX_ROWS).contains(&row)\n    })\n}\nassert!(rows_in_bounds(range), \"rows must be 1..={MAX_ROWS}\");","typeGuard":"fn has_valid_rows(range: &str) -> bool {\n    range.split(':').all(|cell| {\n        let digits: String = cell.chars().skip_while(|c| c.is_ascii_alphabetic()).collect();\n        matches!(digits.parse::<u32>(), Ok(n) if (1..=1_048_576).contains(&n))\n    })\n}","tryCatchPattern":null,"preventionTips":["Rows are 1-based: convert 0-based client coordinates by adding 1.","Clamp end rows to 1_048_576; never request 'whole column' as max-row ranges.","Page through large sheets in bounded windows instead."],"tags":["rust","excel","validation","bounds","range","xlsx"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}