{"record":{"id":"33ce51e501478f06","repo":"iOfficeAI/OfficeCLI","slug":"axis-field-fieldref-index-idx-is-out-of","errorCode":null,"errorMessage":"{axis} field '{fieldRef}' (index {idx}) is out of range after source narrowing to {newFieldCount} column(s). Restate {axis}= in the same Set call to drop or reassign it.","messagePattern":"(.+?) field '(.+?)' \\(index (.+?)\\) is out of range after source narrowing to (.+?) column\\(s\\)\\. Restate (.+?)= in the same Set call to drop or reassign it\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/PivotTableHelper.Readback.cs","lineNumber":442,"sourceCode":"        // crashing RenderPivotIntoSheet with ArgumentOutOfRangeException.\n        // Prefer strict error over data loss: user must explicitly restate the\n        // affected axes in the same Set call if they intended to drop them.\n        var newFieldCount = headers.Length;\n        var existingPivotDef = pivotPart.PivotTableDefinition;\n        if (existingPivotDef != null)\n        {\n            // Axes that the same Set call is explicitly overwriting are\n            // excluded from validation — their new values will be parsed\n            // against the fresh headers by RebuildFieldAreas.\n            bool rowsOverwritten = pendingFieldAreaProps?.ContainsKey(\"rows\") == true;\n            bool colsOverwritten = pendingFieldAreaProps?.ContainsKey(\"cols\") == true;\n            bool valuesOverwritten = pendingFieldAreaProps?.ContainsKey(\"values\") == true;\n            bool filtersOverwritten = pendingFieldAreaProps?.ContainsKey(\"filters\") == true;\n\n            void ValidateIndex(int idx, string axis, string fieldRef)\n            {\n                if (idx >= newFieldCount)\n                    throw new ArgumentException(\n                        $\"{axis} field '{fieldRef}' (index {idx}) is out of range \" +\n                        $\"after source narrowing to {newFieldCount} column(s). \" +\n                        $\"Restate {axis}= in the same Set call to drop or reassign it.\");\n            }\n            if (!valuesOverwritten && existingPivotDef.DataFields != null)\n            {\n                foreach (var df in existingPivotDef.DataFields.Elements<DataField>())\n                {\n                    var fi = (int)(df.Field?.Value ?? 0);\n                    ValidateIndex(fi, \"value\", df.Name?.Value ?? fi.ToString());\n                }\n            }\n            if (!rowsOverwritten && existingPivotDef.RowFields != null)\n            {\n                foreach (var f in existingPivotDef.RowFields.Elements<Field>())\n                {\n                    var fi = f.Index?.Value ?? -1;\n                    if (fi >= 0) ValidateIndex(fi, \"row\", fi.ToString());","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/PivotTableHelper.Readback.cs#L424-L460","documentation":"Thrown by the R15-2 validation in RefreshPivotCacheFromSource: when the new source range has fewer columns than the old one, existing pivot field indices (value/row/col/filter) may point past the new header list. Rather than silently clamping or dropping those fields — which would leave DataFields pointing past columnData and crash RenderPivotIntoSheet with ArgumentOutOfRangeException — the code rejects the operation. Axes that the SAME Set call is explicitly overwriting (rows=/cols=/values=/filters=) are excluded from this check because their new values will be re-parsed against fresh headers.","triggerScenarios":"Calling Set source=Sheet1!A1:B10 on a pivot that currently has values=ColD (index 3) when the new range only has 2 columns — and values= is NOT also passed in the same call. The existing DataField at index 3 exceeds the new field count of 2.","commonSituations":"Narrowing a pivot's source from a wide range to a narrow one without restating which fields go on each axis; dropping columns from the source data while the pivot still references them; restructuring a data table to fewer columns.","solutions":["In the same Set call, restate the affected axis to point at a column that exists in the new range: e.g. Set pivot source=Sheet1!A1:B10 values=ColB.","Widen the source range so existing field indices still fit within the new column count.","Explicitly drop the axis by setting it to empty if you intend to remove it.","Check the new header count first (Get source headers) and compare against the pivot's current field assignments."],"exampleFix":"// before — pivot has values=Amount (index 3), narrowing to 2 cols fails\nSet pivot source=Sheet1!A1:B10\n// after — restate values in the same call\nSet pivot source=Sheet1!A1:B10 values=ColB","handlingStrategy":"validation","validationCode":"// Before narrowing a source, compare existing field indices against the new column count\nvar (newHeaders, _, _) = ReadSourceData(sourceWsPart, newRange);\nint newFieldCount = newHeaders.Length;\nforeach (var df in pivotDef.DataFields?.Elements<DataField>() ?? Enumerable.Empty<DataField>())\n{\n    if ((int)(df.Field?.Value ?? 0) >= newFieldCount && !restattingValues)\n        throw new ArgumentException($\"Data field index {(int)(df.Field?.Value ?? 0)} exceeds new column count {newFieldCount}. Restate values= in the same Set call.\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When narrowing a source range, always restate rows=/cols=/values=/filters= in the same Set call.","Check the new column count against existing field assignments before committing.","If dropping columns intentionally, explicitly set the affected axis to remove the stale reference.","Prefer widening over narrowing when field assignments must be preserved."],"tags":["pivot","excel","source-range","field-index","data-loss-prevention","set-property"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}