tursodatabase/turso · warning · System.Text.Json.JsonException

Extensions array cannot be null.

Error message

Extensions array cannot be null.

What it means

ExtensionsJsonConverter deserializes the Platform API's Extensions union: the string "all", or an array of names. When the JSON token is an array but JsonSerializer.Deserialize<string[]> returns null, it throws JsonException with this message. A StartArray token essentially cannot produce null under normal options, so this is a defensive guard against degenerate input or interfering custom converters.

Source

Thrown at bindings/dotnet/src/Turso.Platform.Client/TursoPlatformClient.Supplement.cs:38

    {
        ArgumentNullException.ThrowIfNull(names);
        return new Extensions(names.ToArray());
    }

    /// <summary>The explicitly enabled extension names, or <see langword="null"/> for <see cref="All"/>.</summary>
    public IReadOnlyList<string>? Names { get; }
}

internal sealed class ExtensionsJsonConverter : System.Text.Json.Serialization.JsonConverter<Extensions>
{
    public override Extensions Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
    {
        return reader.TokenType switch
        {
            System.Text.Json.JsonTokenType.String when reader.GetString() == "all" => Extensions.All,
            System.Text.Json.JsonTokenType.StartArray => Extensions.FromNames(
                System.Text.Json.JsonSerializer.Deserialize<string[]>(ref reader, options)
                ?? throw new System.Text.Json.JsonException("Extensions array cannot be null.")),
            _ => throw new System.Text.Json.JsonException("Extensions must be \"all\" or an array of extension names."),
        };
    }

    public override void Write(System.Text.Json.Utf8JsonWriter writer, Extensions value, System.Text.Json.JsonSerializerOptions options)
    {
        if (value.Names is null)
        {
            writer.WriteStringValue("all");
            return;
        }

        System.Text.Json.JsonSerializer.Serialize(writer, value.Names, options);
    }
}

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Send well-formed payloads: "extensions": ["crypto","regexp"] or "extensions": "all"; avoid null elements.
  2. Build requests with the typed Extensions class (Extensions.All / Extensions.FromNames) instead of hand-writing JSON.
  3. Use plain JsonSerializerOptions (web defaults) when deserializing Platform DTOs so the built-in converter behavior is not overridden.
  4. If you wrap deserialization, validate the parsed array for null entries before proceeding.

Example fix

// before
{ "extensions": [null, "crypto"] }

// after
{ "extensions": ["crypto", "regexp"] }
Defensive patterns

Strategy: validation

Validate before calling

// validate the extensions payload shape before deserializing
using var doc = JsonDocument.Parse(json);
var token = doc.RootElement.GetProperty("extensions");
bool ok = (token.ValueKind == JsonValueKind.String && token.GetString() == "all")
       || (token.ValueKind == JsonValueKind.Array && token.EnumerateArray().All(e => e.ValueKind == JsonValueKind.String));

Try / catch

try { var ext = JsonSerializer.Deserialize<Extensions>(json); }
catch (JsonException) { /* payload malformed: fix producer or reject request */ }

Prevention

When it happens

Trigger: Deserializing a request/response body where "extensions" is an array token that yields null — realistically only with custom JsonSerializerOptions string[] converters, malformed framing, or a hand-rolled reader pipeline; ordinary payloads like [] or ["crypto"] never take this path.

Common situations: Unit tests that share a JsonSerializerOptions instance with custom converters; intermediate middleware that rewrites bodies; virtually never seen from the real Platform API.

Related errors


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