{"record":{"id":"157b59bc30ea040a","repo":"larksuite/cli","slug":"range-needs-a-maximum-column-range-ref","errorCode":null,"errorMessage":"Range needs a maximum column: {range_ref}","messagePattern":"Range needs a maximum column: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/lark-sheets/scripts/lark_sheet_range.py","lineNumber":111,"sourceCode":"        raise ValueError(f\"Invalid A1 range: {range_ref}\")\n    start = _parse_endpoint(parts[0])\n    end = _parse_endpoint(parts[-1])\n\n    if len(parts) == 1:\n        if start[0] != \"cell\":\n            raise ValueError(f\"A1 range must include a cell or ':' separator: {range_ref}\")\n        _, row, col = start\n        return RangeBounds(row, col, row, col)\n\n    if start[0] == end[0] == \"cell\":\n        _, start_row, start_col = start\n        _, end_row, end_col = end\n    elif start[0] == end[0] == \"row\":\n        _, start_row = start\n        _, end_row = end\n        start_col = 1\n        if max_col is None:\n            raise ValueError(f\"Range needs a maximum column: {range_ref}\")\n        end_col = max_col\n    elif start[0] == end[0] == \"column\":\n        _, start_col = start\n        _, end_col = end\n        start_row = 1\n        if max_row is None:\n            raise ValueError(f\"Range needs a maximum row: {range_ref}\")\n        end_row = max_row\n    elif start[0] == \"cell\" and end[0] == \"column\":\n        _, start_row, start_col = start\n        _, end_col = end\n        if max_row is None:\n            raise ValueError(f\"Range needs a maximum row: {range_ref}\")\n        end_row = max(start_row, max_row)\n    elif start[0] == \"cell\" and end[0] == \"row\":\n        _, start_row, start_col = start\n        _, end_row = end\n        if max_col is None:","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/skills/lark-sheets/scripts/lark_sheet_range.py#L93-L129","documentation":"parse_range() raises this when an open-ended row range like 'Sheet1!3:5' is given but the caller did not pass max_col. A row-only range has no right edge, so the library refuses to guess a spreadsheet-wide limit and requires the caller's actual grid width to make the range finite.","triggerScenarios":"Calling parse_range('3:5') (or 'A3:5' style row endpoints) without the max_col keyword, or calling one of its wrappers (parse_annotated_csv, _external_merge_anchors, build_occupancy, profile_grid) with a row-range but no known grid width.","commonSituations":"Handing a rows-only A1 range from a config file or user input to a CSV-export helper that only knows dimensions after fetching data; scripting '+csv-get' with a range like '1:10' when the script forgot to pass the fetched sheet's column count.","solutions":["Pass max_col explicitly: parse_range('3:5', max_col=10) or the actual column count from the fetched grid.","Use a fully-closed cell range instead, e.g. parse_range('A3:J5'), which needs no max_col.","If dimensions are unknown, fetch the grid first (sheet metadata / returned rows) and then call parse_range with those dimensions."],"exampleFix":"// before\nbounds = parse_range(\"3:5\")\n// after\nbounds = parse_range(\"3:5\", max_col=len(header_row))  # or max_col=26","handlingStrategy":"validation","validationCode":"from lark_sheet_range import parse_range, _parse_endpoint\n\ndef is_open_row_range(ref: str) -> bool:\n    \"\"\"True if ref is row:row form and will require max_col.\"\"\"\n    body = ref.strip().rsplit(\"!\", 1)[-1]\n    parts = body.split(\":\")\n    return len(parts) == 2 and all(_looks_row(p) for p in parts)\n\ndef _looks_row(p: str) -> bool:\n    p = p.lstrip(\"$\")\n    return p.isdigit() and int(p) >= 1\n\n# call site\nif is_open_row_range(ref):\n    assert max_col is not None, f\"{ref} needs max_col\"\nbounds = parse_range(ref, max_col=max_col)","typeGuard":"def needs_max_col(ref: str) -> bool:\n    body = ref.strip().rsplit(\"!\", 1)[-1]\n    parts = body.split(\":\")\n    if len(parts) != 2:\n        return False\n    import re\n    row_re = re.compile(r\"^\\$?([1-9][0-9]*)$\")\n    return bool(row_re.match(parts[0].strip()) and row_re.match(parts[-1].strip()))","tryCatchPattern":"try:\n    bounds = parse_range(ref, max_col=max_col)\nexcept ValueError as e:\n    if \"maximum column\" in str(e):\n        bounds = parse_range(ref, max_col=grid_col_count)  # retry with fetched width\n    else:\n        raise","preventionTips":["Prefer fully closed cell:cell ranges (e.g. A3:J5) in configs and scripts","Thread the fetched grid dimensions (row/col counts) into every parse_range call","Unit-test range parsing with the exact strings users supply, including sheet-name prefixes"],"tags":["python","validation","a1-range","sheets"],"backgroundTag":"open-ended-range-missing-dimension","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}