tursodatabase/turso · error · InvalidCastException

Cannot convert remote {value.Type} value to DateTime.

Error message

Cannot convert remote {value.Type} value to DateTime.

What it means

TursoRemoteDataReader.GetDateTime only accepts values whose remote (Hrana) wire type is exactly "text", which it parses with DateTime.Parse using the invariant culture. Any other wire type (integer, float, blob, null) throws InvalidCastException naming the type. SQLite has no DateTime storage class, so dates must travel as ISO-8601 text for this getter to work.

Source

Thrown at bindings/dotnet/src/Turso.Data/TursoRemoteDataReader.cs:101

        EnsureOpen();
        ValidateOrdinal(ordinal);

        var declaredType = ordinal < CurrentResult.Columns.Count
            ? CurrentResult.Columns[ordinal].DeclType
            : null;
        if (!string.IsNullOrWhiteSpace(declaredType))
            return declaredType;

        var value = FirstNonNullValue(ordinal);
        return value is null ? string.Empty : GetTypeName(value.Type);
    }

    public override DateTime GetDateTime(int ordinal)
    {
        var value = CurrentValue(ordinal);
        return value.Type == "text"
            ? DateTime.Parse((string)value.ToClrValue(), CultureInfo.InvariantCulture)
            : throw new InvalidCastException($"Cannot convert remote {value.Type} value to DateTime.");
    }

    public override decimal GetDecimal(int ordinal)
    {
        return CurrentValue(ordinal).GetDecimal();
    }

    public override double GetDouble(int ordinal)
    {
        return CurrentValue(ordinal).GetDouble();
    }

    public override Type GetFieldType(int ordinal)
    {
        EnsureOpen();
        ValidateOrdinal(ordinal);

        if (ordinal < CurrentResult.Columns.Count

View on GitHub (pinned to 6c72522679)

Solutions

  1. Store and select dates as ISO-8601 TEXT (e.g. datetime('iso8601') / parameterize DateTime values, which bind as text) so the wire type is text.
  2. If the column is numeric, read it with GetValue/GetInt64 and convert manually (DateTimeOffset.FromUnixTimeSeconds for epoch values, or the Julian-day formula for REAL).
  3. Guard with IsDBNull(ordinal) before calling GetDateTime to handle NULL timestamps separately.
  4. Inspect reader.GetDataTypeName(ordinal) or GetFieldType(ordinal) per row when the schema is not under your control, and branch on the reported type.

Example fix

// before
DateTime created = reader.GetDateTime(2); // column holds unix-epoch integer -> InvalidCastException

// after
object raw = reader.GetValue(2);
DateTime created = raw switch
{
    string s => DateTime.Parse(s, CultureInfo.InvariantCulture),
    long epoch => DateTimeOffset.FromUnixTimeSeconds(epoch).UtcDateTime,
    DBNull => throw new InvalidOperationException("created must not be null"),
    _ => throw new InvalidCastException()
};
Defensive patterns

Strategy: validation

Validate before calling

// before GetDateTime: confirm the cell is text and not null
bool CanGetDateTime(DbDataReader reader, int ordinal) =>
    !reader.IsDBNull(ordinal) && reader.GetDataTypeName(ordinal) == "TEXT";

Type guard

static bool IsTextColumn(DbDataReader r, int ord) => r.GetDataTypeName(ord).Equals("TEXT", StringComparison.OrdinalIgnoreCase);

Try / catch

try { var dt = reader.GetDateTime(i); }
catch (InvalidCastException) { var raw = reader.GetValue(i); /* convert epoch/Julian manually */ }

Prevention

When it happens

Trigger: Calling reader.GetDateTime(ordinal) on a column whose current value is not text: a unix-epoch INTEGER produced by strftime('%s',...), a Julian-day REAL, a blob, or NULL. The type check is on the per-cell wire type, so even a declared TEXT column throws if the stored value for that row is numeric.

Common situations: A table written by another app or language that stores dates as unix epoch integers or REAL Julian day numbers; migrating schema from a server database that used native date types; NULL timestamps on optional columns; EF or Dapper materializers that map DateTime without a value converter.

Related errors


AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-08-20). Data as JSON: /api/errors/3251791f0331e64c. Report an issue: GitHub.