tursodatabase/turso · error · JsonException

Unexpected token for last_insert_rowid: {reader.TokenType}

Error message

Unexpected token for last_insert_rowid: {reader.TokenType}

What it means

last_insert_rowid in a Hrana response may be serialized as either a JSON string or a number, so the client registers a custom JsonConverter that accepts both token types. If the server emits any other token (boolean, object, start-of-array), System.Text.Json raises this JsonException because the converter has no valid value to produce.

Source

Thrown at bindings/dotnet/src/Turso.Serverless.Client/HranaProtocol.cs:355

    [JsonPropertyName("is_explain")]
    public bool IsExplain { get; set; }

    [JsonPropertyName("is_readonly")]
    public bool IsReadonly { get; set; }
}

/// <summary>The server sends <c>last_insert_rowid</c> as either a JSON string or a number.</summary>
internal sealed class LongFromStringOrNumberConverter : JsonConverter<long?>
{
    public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return reader.TokenType switch
        {
            JsonTokenType.Null => null,
            JsonTokenType.String => long.Parse(reader.GetString()!, CultureInfo.InvariantCulture),
            JsonTokenType.Number => reader.GetInt64(),
            _ => throw new JsonException($"Unexpected token for last_insert_rowid: {reader.TokenType}"),
        };
    }

    public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options)
    {
        if (value is null)
        {
            writer.WriteNullValue();
        }
        else
        {
            writer.WriteNumberValue(value.Value);
        }
    }
}

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Verify options.Url points at the real Turso/libsql Hrana endpoint (libsql://... or https URL of the database)
  2. Log the raw response body when the exception surfaces to see which token actually arrived
  3. Update Turso.Serverless.Client to the latest version for broader converter coverage
  4. If a reverse proxy sits in front of Turso, configure it to pass JSON bodies through unmodified
Defensive patterns

Strategy: try-catch

Try / catch

try { var rs = await conn.ExecuteAsync(sql, cmd, timeout, ct); } catch (JsonException ex) when (ex.Message.Contains("last_insert_rowid")) { log.RawResponse(); throw new TursoServerlessException("Server sent an unexpected last_insert_rowid payload; check endpoint URL and versions", ex); }

Prevention

When it happens

Trigger: The response element containing last_insert_rowid arrives as true, an object, or a truncated body; typically caused by a gateway/proxy rewriting the body, a non-Hrana endpoint returning JSON in a different shape, or a version-skewed server emitting a new representation the converter does not know.

Common situations: options.Url pointing at a load balancer or edge worker that mangles responses; HTML error pages parsed as JSON during outages; mixing an old client with a newer server build that changed the field shape.

Understand the failure class

Related errors


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