{"record":{"id":"1e43d7fb61f40914","repo":"iOfficeAI/OfficeCLI","slug":"field-index-roundtripfieldidx-value-out-of-range","errorCode":null,"errorMessage":"field index {roundTripFieldIdx.Value} out of range (0..{headers.Length - 1})","messagePattern":"field index (.+?) out of range \\(0\\.\\.(.+?)\\)","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/PivotTableHelper.Parse.cs","lineNumber":335,"sourceCode":"            // func[0] below. This keeps the showAs slot positionally addressable.\n            if (string.IsNullOrEmpty(func)) func = \"sum\";\n\n            // CONSISTENCY(aggregate-override): if aggregate=<list> was passed\n            // and has an entry at this position, it wins over the colon form.\n            if (aggregateOverrides != null && specIndex < aggregateOverrides.Length\n                && !string.IsNullOrEmpty(aggregateOverrides[specIndex]))\n                func = aggregateOverrides[specIndex];\n\n            int fieldIdx = -1;\n            // CONSISTENCY(pivot-roundtrip / R9-2): when the Get readback shape\n            // gave us an explicit numeric cacheField index, prefer it over the\n            // (possibly stripped) display name. This makes Set values=GetOutput\n            // robust even if the source headers were renamed between Get and\n            // Set, and removes any ambiguity from the prefix-strip heuristic.\n            if (roundTripFieldIdx.HasValue)\n            {\n                if (roundTripFieldIdx.Value < 0 || roundTripFieldIdx.Value >= headers.Length)\n                    throw new ArgumentException(\n                        $\"field index {roundTripFieldIdx.Value} out of range (0..{headers.Length - 1})\");\n                fieldIdx = roundTripFieldIdx.Value;\n            }\n            else if (int.TryParse(fieldName, out var idx))\n            {\n                // CONSISTENCY(strict-enums / R8-6): a numeric token is a\n                // column index. Out-of-range indices used to silently drop\n                // the value-field, producing an empty pivot with no error.\n                // Reject up front with the available-index range so users\n                // catch the typo immediately (mirrors the throw used for\n                // unknown field names).\n                if (idx < 0 || idx >= headers.Length)\n                    throw new ArgumentException(\n                        $\"field index {idx} out of range (0..{headers.Length - 1})\");\n                fieldIdx = idx;\n            }\n            else\n            {","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/PivotTableHelper.Parse.cs#L317-L353","documentation":"When a values= token is in the Get-readback shape (it carries an explicit cacheField index in slot 3), the parser trusts that index over the display name to make Set-after-Get robust to header renames. This throw fires when that round-trip index falls outside the current header array bounds — typically because the source range was narrowed or re-ordered between Get and Set. The message reports the allowed 0..N-1 range so the user can see how far off the cached index is.","triggerScenarios":"Get a pivot, then change the source range to have fewer columns, then paste the Get output back into Set; re-importing a Get dump into a workbook whose source columns were deleted; replaying a recorded spec against a template with a different column count.","commonSituations":"Round-trip workflows where the underlying sheet structure changed between read and write; automation that captures a pivot spec and replays it against a sibling workbook with a smaller schema.","solutions":["Restate the values= tokens against the new headers (drop the slot-3 index) so the name-based resolver runs instead","Ensure the source range you Set against has at least as many columns as the one you Got from","Re-Get the pivot from the current source to refresh the cached indices before Set"],"exampleFix":"// before (stale index 7 in a sheet that now has only 5 columns)\nvalues=\"Sales:sum:7\"\n// after (drop the stale index, let the name resolver bind)\nvalues=\"Sales:sum\"","handlingStrategy":"validation","validationCode":"// Drop stale round-trip indices when the source column count may have changed\nvar cleaned = valuesSpecs.Select(s => Regex.Replace(s, @\":\\d+$\", \"\")); // strip trailing :<index>\n// or re-Get from the current source to refresh indices","typeGuard":"static bool RoundTripIndexInBounds(int idx, int headerCount) =>\n    idx >= 0 && idx < headerCount;","tryCatchPattern":"try { SetPivot(props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"out of range\"))\n{ /* strip slot-3 indices and retry, or re-Get against current source */ }","preventionTips":["Do not replay a Get dump after changing the source column count","Strip the trailing index from values= tokens before replay if schema may differ","Re-Get the pivot against the current source to refresh round-trip indices"],"tags":["pivottable","round-trip","field-resolution","argument-validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}