tursodatabase/turso · error · ArgumentException

Offset and count were out of bounds for the array.

Error message

Offset and count were out of bounds for the array.

What it means

Every Read/Write first calls ValidateBuffer, which enforces the standard bounds contract: buffer non-null, offset/count non-negative, and offset <= buffer.Length && count <= buffer.Length - offset. Any violation throws ArgumentException (Properties.Resources.InvalidOffsetAndCount) before any I/O happens, matching System.IO's span rules.

Source

Thrown at bindings/dotnet/src/Turso.Data.Sqlite/SqliteBlob.cs:158

        var value = command.ExecuteScalar();
        return value switch
        {
            byte[] bytes => bytes,
            string text => Encoding.UTF8.GetBytes(text),
            null or DBNull => throw new SqliteException(Properties.Resources.SqliteNativeError(1, "no such rowid: " + rowId), 1),
            _ => Encoding.UTF8.GetBytes(Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture) ?? string.Empty)
        };
    }

    private static void ValidateBuffer(byte[] buffer, int offset, int count)
    {
        ArgumentNullException.ThrowIfNull(buffer);
        if (offset < 0)
            throw new ArgumentOutOfRangeException(nameof(offset), offset, message: null);
        if (count < 0)
            throw new ArgumentOutOfRangeException(nameof(count), count, message: null);
        if (offset > buffer.Length || count > buffer.Length - offset)
            throw new ArgumentException(Properties.Resources.InvalidOffsetAndCount);
    }

    private static string QuoteIdentifier(string identifier)
        => "\"" + identifier.Replace("\"", "\"\"", StringComparison.Ordinal) + "\"";
}

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Derive count from the remaining space: count = Math.Min(requested, buffer.Length - offset).
  2. Prefer modern APIs (stream.ReadExactly, spans/ArraySegment) that remove manual bounds math.
  3. When it throws, log buffer.Length/offset/count at the call site - the values are always the fastest clue.

Example fix

// before
int read = blob.Read(buf, offset: 4, count: buf.Length); // 4 + buf.Length > buf.Length -> throws

// after
int read = blob.Read(buf, 4, buf.Length - 4);
Defensive patterns

Strategy: validation

Validate before calling

static void CheckBounds(byte[] buffer, int offset, int count) {
    ArgumentNullException.ThrowIfNull(buffer);
    if (offset < 0 || count < 0 || offset > buffer.Length || count > buffer.Length - offset)
        throw new ArgumentException("offset/count out of bounds");
}

CheckBounds(buf, off, cnt);
blob.Read(buf, off, cnt);

Prevention

When it happens

Trigger: Passing a count larger than the space remaining after offset (the classic off-by-one: count = buffer.Length while offset > 0); negative offset/count from unsigned underflow or parsed input; an offset past the end of the buffer.

Common situations: Looping reads that keep an offset cursor but pass the full length as count; (buffer, 0, int.MaxValue) 'read everything' idioms; pointer-arithmetic code translated from C/C++; header-size math off by one.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/76eb9625ae2f63ea. Report an issue: GitHub.