{"record":{"id":"0c36c7e83c92f8ef","repo":"dotnet/machinelearning","slug":"strings-spansmultiplebuffers","errorCode":null,"errorMessage":"Strings.SpansMultipleBuffers","messagePattern":"Strings\\.SpansMultipleBuffers","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.Data.Analysis/DataFrameColumns/ArrowStringDataFrameColumn.cs","lineNumber":357,"sourceCode":"        {\n            int nullCount = 0;\n            for (long i = startIndex; i < numberOfRows; i++)\n            {\n                if (!IsValid(i))\n                    nullCount++;\n            }\n            return nullCount;\n        }\n\n        /// <inheritdoc/>\n        protected internal override Apache.Arrow.Array ToArrowArray(long startIndex, int numberOfRows)\n        {\n            if (numberOfRows == 0)\n                return new StringArray(numberOfRows, ArrowBuffer.Empty, ArrowBuffer.Empty, ArrowBuffer.Empty);\n            int offsetsBufferIndex = GetBufferIndexContainingRowIndex(startIndex, out int indexInBuffer);\n            if (numberOfRows != 0 && numberOfRows > _offsetsBuffers[offsetsBufferIndex].Length - 1 - indexInBuffer)\n            {\n                throw new ArgumentException(Strings.SpansMultipleBuffers, nameof(numberOfRows));\n            }\n            ArrowBuffer dataBuffer = new ArrowBuffer(_dataBuffers[offsetsBufferIndex].ReadOnlyBuffer);\n            ArrowBuffer offsetsBuffer = new ArrowBuffer(_offsetsBuffers[offsetsBufferIndex].ReadOnlyBuffer);\n            ArrowBuffer nullBuffer = new ArrowBuffer(_nullBitMapBuffers[offsetsBufferIndex].ReadOnlyBuffer);\n            int nullCount = GetNullCount(indexInBuffer, numberOfRows);\n            return new StringArray(numberOfRows, offsetsBuffer, dataBuffer, nullBuffer, nullCount, indexInBuffer);\n        }\n\n        protected internal override PrimitiveDataFrameColumn<long> GetSortIndices(bool ascending, bool putNullValuesLast) => throw new NotSupportedException();\n\n        public new ArrowStringDataFrameColumn Clone(long numberOfNullsToAppend = 0)\n        {\n            return (ArrowStringDataFrameColumn)CloneImplementation(numberOfNullsToAppend);\n        }\n\n        public new ArrowStringDataFrameColumn Clone(DataFrameColumn mapIndices, bool invertMapIndices = false, long numberOfNullsToAppend = 0)\n        {\n            return (ArrowStringDataFrameColumn)CloneImplementation(mapIndices, invertMapIndices, numberOfNullsToAppend);","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/dotnet/machinelearning/blob/7b76e69cf964daeca3f1377af6bc5543284d56c6/src/Microsoft.Data.Analysis/DataFrameColumns/ArrowStringDataFrameColumn.cs#L339-L375","documentation":"ToArrowArray produces an Arrow StringArray backed by a single contiguous buffer. If the requested window [startIndex, startIndex + numberOfRows) extends past the end of the buffer containing startIndex, the library throws ArgumentException(Strings.SpansMultipleBuffers, nameof(numberOfRows)), because an Arrow StringArray cannot be built from a window spanning two buffers.","triggerScenarios":"Calling ToArrowArray(startIndex, numberOfRows) where numberOfRows exceeds the remaining values in startIndex's buffer: numberOfRows > _offsetsBuffers[offsetsBufferIndex].Length - 1 - indexInBuffer (and numberOfRows != 0).","commonSituations":"Exporting whole columns that internally consist of multiple appended buffers (each Append with > 0 free space adds buffers); batch-size loops that assume one buffer per column; columns built incrementally then converted to Arrow.","solutions":["Split the export into per-buffer chunks sized to fit within the buffer containing startIndex.","Use Clone/GetValue-based export (e.g. GetValues) instead of ToArrowArray when the data may span buffers.","Build the column in a single constructor call (one data buffer) if zero-copy Arrow export matters.","Check buffer boundaries via the internal offsets buffers before requesting a window."],"exampleFix":"// before\nvar arr = col.ToArrowArray(startIndex: 0, numberOfRows: (int)col.Length); // may span buffers\n\n// after\nlong remaining = (int)col.Length; long idx = 0;\nwhile (remaining > 0)\n{\n    int chunk = (int)Math.Min(remaining, col.Length /* buffer-bounded chunk */);\n    var arr = col.ToArrowArray(idx, chunk);\n    idx += chunk; remaining -= chunk;\n}","handlingStrategy":"validation","validationCode":"// ensure the window fits inside the buffer containing startIndex\nint bufIdx = GetBufferIndexContainingRowIndex(startIndex, out int idxInBuf);\nif (numberOfRows > bufLengths[bufIdx] - 1 - idxInBuf)\n    throw new ArgumentException(\"Window spans multiple buffers\");","typeGuard":null,"tryCatchPattern":"try { var arr = col.ToArrowArray(start, count); }\ncatch (ArgumentException) { /* fall back to per-buffer chunked export */ }","preventionTips":["Chunk exports to buffer boundaries","Avoid mixing single-buffer constructors with Append on the same column if zero-copy export matters","Prefer Clone/GetValues for whole-column export","Track buffer counts when building columns incrementally"],"tags":["dotnet","arrow","dataframe","argument-exception"],"backgroundTag":"invalid-argument-value","analyzedSha":"7b76e69cf964daeca3f1377af6bc5543284d56c6","analyzedAt":"2026-09-11T12:35:38.930Z","contentChangedAt":"2026-09-11T12:35:38.930Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}