{"record":{"id":"ad63527c3909e313","repo":"larksuite/cli","slug":"s-takes-a-single-range-per-sub-op-got-d-entri","errorCode":null,"errorMessage":"%s takes a single \"range\" per sub-op, got %d entries in %q — split them into %d sub-ops (one per range)","messagePattern":"(.+?) takes a single \"range\" per sub-op, got (.+?) entries in %q — split them into (.+?) sub-ops \\(one per range\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/sheets/batch_op_dispatch.go","lineNumber":504,"sourceCode":"\t\t\t\t// and make that spelling's own turn read as a conflict.\n\t\t\t\tcanonical[target] = taken\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"%s got both %q and %q, which are two names for the same flag, with different values — keep %q\", sc, k, taken, taken) //nolint:forbidigo // intermediate error; the batch dispatcher wraps it into a typed operations validation error\n\t\t}\n\t\tif strings.ToLower(hv) == \"ranges\" && vocab[\"range\"] && !vocab[\"ranges\"] {\n\t\t\tif _, taken := input[\"range\"]; taken {\n\t\t\t\treturn fmt.Errorf(\"%s got both %q and \\\"range\\\" — keep \\\"range\\\" and drop %q\", sc, k, k) //nolint:forbidigo // intermediate error; the batch dispatcher wraps it into a typed operations validation error\n\t\t\t}\n\t\t\tif arr, isArr := input[k].([]interface{}); isArr {\n\t\t\t\tif len(arr) == 1 {\n\t\t\t\t\tif s, isStr := arr[0].(string); isStr {\n\t\t\t\t\t\tinput[\"range\"] = s\n\t\t\t\t\t\tdelete(input, k)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"%s takes a single \\\"range\\\" per sub-op, got %d entries in %q — split them into %d sub-ops (one per range)\", sc, len(arr), k, len(arr)) //nolint:forbidigo // intermediate error; the batch dispatcher wraps it into a typed operations validation error\n\t\t\t}\n\t\t\tif s, isStr := input[k].(string); isStr {\n\t\t\t\tinput[\"range\"] = s\n\t\t\t\tdelete(input, k)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tmsg := fmt.Sprintf(\"unknown input key %q\", k)\n\t\tdisplay := make([]string, 0, len(vocab))\n\t\tfor name := range vocab {\n\t\t\tdisplay = append(display, strings.ReplaceAll(name, \"-\", \"_\"))\n\t\t}\n\t\tsort.Strings(display)\n\t\tif match := suggest.Closest(strings.ToLower(hv), display, 1); len(match) > 0 {\n\t\t\tmsg += fmt.Sprintf(\" — did you mean %q?\", match[0])\n\t\t}\n\t\treturn fmt.Errorf(\"%s\", msg) //nolint:forbidigo // intermediate error; the batch dispatcher wraps it into a typed operations validation error\n\t}","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/sheets/batch_op_dispatch.go#L486-L522","documentation":"Each batch sub-op takes exactly one `range`. If the `range`-typed key holds an array of multiple entries, the dispatcher does not guess how to distribute them and errors, telling you how many sub-ops to create instead.","triggerScenarios":"Passing `\"range\": [\"Sheet1!A1:B2\", \"Sheet1!C1:D2\"]` (a multi-entry array) in one sub-op input of a sheets batch update. A single-element string array is auto-unwrapped; only arrays with more than one entry fail.","commonSituations":"Scripts that collected multiple ranges into one array and passed it to a single set/update op; migrating from APIs that accepted `ranges: [...]` per op to the one-range-per-sub-op batch format.","solutions":["Split the array into multiple sub-ops, one per range, as the error message instructs.","If only one range is needed, pass it as a plain string, not an array.","If a single-element array was passed with non-string contents, fix the element type to a string."],"exampleFix":"// before\n{\"op\": \"set\", \"range\": [\"A1:B2\", \"C1:D2\"], \"values\": [...]}\n// after\n[{\"op\": \"set\", \"range\": \"A1:B2\", \"values\": [...]}, {\"op\": \"set\", \"range\": \"C1:D2\", \"values\": [...]}]","handlingStrategy":"validation","validationCode":"func expandRanges(subOp map[string]interface{}) ([]map[string]interface{}, error) {\n    switch v := subOp[\"range\"].(type) {\n    case string:\n        return []map[string]interface{}{subOp}, nil\n    case []interface{}:\n        if len(v) <= 1 { return []map[string]interface{}{subOp}, nil }\n        out := make([]map[string]interface{}, 0, len(v))\n        for _, r := range v {\n            c := maps.Clone(subOp)\n            c[\"range\"] = r\n            out = append(out, c)\n        }\n        return out, nil\n    default:\n        return nil, fmt.Errorf(\"range must be string or []string\")\n    }\n}","typeGuard":"func isSingleRange(v interface{}) bool {\n    if s, ok := v.(string); ok { return s != \"\" }\n    if arr, ok := v.([]interface{}); ok { return len(arr) == 1 }\n    return false\n}","tryCatchPattern":null,"preventionTips":["One range per sub-op: expand arrays into multiple sub-ops before dispatch.","Pass ranges as plain strings, never arrays, when only one is needed.","Validate range values with a ranges-string parser before sending."],"tags":["sheets","validation","batch-operations"],"backgroundTag":"invalid-parameter-shape","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"}