dotnet/machinelearning · error · System.ArgumentException

Expected a seekable stream

Error message

Expected a seekable stream

What it means

LoadCsv requires a seekable stream because the CSV reader needs to rewind/re-read rows for header detection and type guessing. If csvStream.CanSeek is false, it throws ArgumentException(Strings.NonSeekableStream, nameof(csvStream)) = 'Expected a seekable stream'. Non-seekable streams (e.g. network or stdin streams) cannot be used directly.

Source

Thrown at src/Microsoft.Data.Analysis/DataFrame.IO.cs:576

        /// <param name="dataTypes">column types (can be empty)</param>
        /// <param name="numberOfRowsToRead">number of rows to read not including the header(if present)</param>
        /// <param name="guessRows">number of rows used to guess types</param>
        /// <param name="addIndexColumn">add one column with the row index</param>
        /// <param name="encoding">The character encoding. Defaults to UTF8 if not specified</param>
        /// <param name="renameDuplicatedColumns">If set to true, columns with repeated names are auto-renamed.</param>
        /// <param name="cultureInfo">culture info for formatting values</param>
        /// <param name="guessTypeFunction">function used to guess the type of a column based on its values</param>
        /// <returns><see cref="DataFrame"/></returns>
        public static DataFrame LoadCsv(Stream csvStream,
                                char separator = ',', bool header = true,
                                string[] columnNames = null, Type[] dataTypes = null,
                                long numberOfRowsToRead = -1, int guessRows = 10, bool addIndexColumn = false,
                                Encoding encoding = null, bool renameDuplicatedColumns = false, CultureInfo cultureInfo = null,
                                Func<IEnumerable<string>, Type> guessTypeFunction = null)
        {
            if (!csvStream.CanSeek)
            {
                throw new ArgumentException(Strings.NonSeekableStream, nameof(csvStream));
            }

            if (dataTypes == null && guessRows <= 0)
            {
                throw new ArgumentException(string.Format(Strings.ExpectedEitherGuessRowsOrDataTypes, nameof(guessRows), nameof(dataTypes)));
            }

            WrappedStreamReaderOrStringReader wrappedStreamReaderOrStringReader = new WrappedStreamReaderOrStringReader(csvStream, encoding ?? Encoding.UTF8);
            return ReadCsvLinesIntoDataFrame(wrappedStreamReaderOrStringReader, separator, header, columnNames, dataTypes, numberOfRowsToRead, guessRows, addIndexColumn, renameDuplicatedColumns, cultureInfo, guessTypeFunction);
        }

        /// <summary>
        /// Writes a DataFrame into a CSV.
        /// </summary>
        /// <param name="dataFrame"><see cref="DataFrame"/></param>
        /// <param name="path">CSV file path</param>
        /// <param name="separator">column separator</param>
        /// <param name="header">has a header or not</param>

View on GitHub (pinned to 7b76e69cf9)

Solutions

  1. Copy the stream into a MemoryStream first (stream.CopyTo(ms); ms.Position = 0), then LoadCsv from the MemoryStream
  2. Write the input to a temporary file and use the file-path LoadCsv overload
  3. If the source is HTTP, read the full body into memory or a file before parsing
  4. Buffer with a seekable wrapper only if buffering is acceptable memory-wise; otherwise stream-parse manually

Example fix

// before
var df = DataFrame.LoadCsv(responseStream);
// after
using var ms = new MemoryStream();
responseStream.CopyTo(ms);
ms.Position = 0;
var df = DataFrame.LoadCsv(ms);
Defensive patterns

Strategy: validation

Validate before calling

if (!stream.CanSeek) { var ms = new MemoryStream(); stream.CopyTo(ms); ms.Position = 0; stream = ms; }

Try / catch

try { return DataFrame.LoadCsv(csvStream); } catch (ArgumentException ex) when (ex.Message.Contains("seekable")) { using var ms = new MemoryStream(); csvStream.CopyTo(ms); ms.Position = 0; return DataFrame.LoadCsv(ms); }

Prevention

When it happens

Trigger: Passing a non-seekable Stream — the raw body stream of an HTTP response, Console.OpenStandardInput(), a decryption stream, or a pipe — to DataFrame.LoadCsv.

Common situations: Reading CSV directly from HttpResponse content stream or a WebSocket; piping data via stdin; wrapping a decompressing GZipStream over a network stream where CanSeek is false.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of dotnet/machinelearning@7b76e69cf9 (2026-09-11). Data as JSON: /api/errors/746e3693263d4973. Report an issue: GitHub.