tursodatabase/turso · error · TursoException

Remote request returned an empty response.

Error message

Remote request returned an empty response.

What it means

The remote Turso client POSTs a JSON pipeline request to the /v2/pipeline endpoint of the URL in your connection string. This error means the server replied with a success status code, but the body deserialized to a null RemotePipelineResponse. System.Text.Json only returns null here when the body is the literal JSON token null, so something answered 200 with a null body instead of a pipeline envelope.

Source

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

        if (_authToken is not null)
            httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authToken);

        using var response = await _httpClient
            .SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, effectiveCancellationToken)
            .ConfigureAwait(false);

        var body = await response.Content.ReadAsStringAsync(effectiveCancellationToken).ConfigureAwait(false);
        if (!response.IsSuccessStatusCode)
        {
            throw new TursoException(
                $"Remote request failed with HTTP {(int)response.StatusCode} {response.ReasonPhrase}: {body}");
        }

        try
        {
            return JsonSerializer.Deserialize<RemotePipelineResponse>(body, JsonOptions)
                   ?? throw new TursoException("Remote request returned an empty response.");
        }
        catch (JsonException ex)
        {
            throw new TursoException($"Unable to parse remote response: {ex.Message}");
        }
    }

    private static CancellationTokenSource? CreateTimeout(int commandTimeout, CancellationToken cancellationToken)
    {
        if (commandTimeout <= 0)
            return null;

        var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        timeout.CancelAfter(TimeSpan.FromSeconds(commandTimeout));
        return timeout;
    }

    private static void ValidateAuthTokenTransport(Uri endpoint, string? authToken)

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Verify the connection string Url points at the actual database endpoint and test it directly: curl -X POST <Url>/v2/pipeline -H 'content-type: application/json' -d '{"requests":[]}' and inspect the raw body.
  2. If a reverse proxy or gateway sits in front of the database, bypass it or fix its routing/rewrite rules for the /v2/pipeline path.
  3. Disable any edge middleware (bot protection, auth edge, WAF) that can answer 200 with an empty or null body on this route.
  4. If the failure is intermittent, treat it as transient: close and reopen the connection to get a fresh session, then retry the command once.
  5. If a raw 'null' body is reproducible against the database itself, report it as a server bug with the request body attached.

Example fix

// before
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT count(*) FROM users";
var count = await cmd.ExecuteScalarAsync();

// after -- retry once on an empty remote response, reopening the session first
await using var cmd = conn.CreateCommand();
cmd.CommandText = "SELECT count(*) FROM users";
object? count;
for (var attempt = 0; ; attempt++)
{
    try
    {
        count = await cmd.ExecuteScalarAsync();
        break;
    }
    catch (TursoException) when (attempt == 0)
    {
        await conn.CloseAsync();
        await conn.OpenAsync(); // fresh session/baton
    }
}
Defensive patterns

Strategy: retry

Validate before calling

var uri = new Uri(builder.Url);
using var probe = new HttpClient();
using var resp = await probe.PostAsync(
    new Uri(uri, "/v2/pipeline"),
    new StringContent("{\"requests\":[]}", Encoding.UTF8, "application/json"));
var body = await resp.Content.ReadAsStringAsync();
if (!resp.IsSuccessStatusCode || body.Trim() is "null" or "")
    throw new InvalidOperationException($"{uri} does not speak the Turso pipeline protocol.");

Try / catch

try
{
    result = await cmd.ExecuteScalarAsync(cancellationToken);
}
catch (TursoException ex) when (ex.Message.Contains("empty response"))
{
    // session may be wedged: dispose, reopen, retry once
    await conn.CloseAsync();
    conn.Dispose();
    conn = new TursoConnection(cs);
    await conn.OpenAsync(cancellationToken);
    result = await cmd.ExecuteScalarAsync(cancellationToken);
}

Prevention

When it happens

Trigger: Any TursoCommand Execute* call (ExecuteReaderAsync, ExecuteNonQueryAsync, ExecuteScalarAsync) or TursoBatch execution on a connection opened with a remote Url= connection string. The call routes through TursoRemoteClient.ExecuteAsync/ExecuteBatchAsync -> SendPipelineAsync, and JsonSerializer.Deserialize<RemotePipelineResponse>(body) returns null because the 2xx body is the JSON literal 'null'.

Common situations: The Url points at a load balancer, API gateway, or dashboard domain that answers 200 with null/empty JSON instead of the database; a reverse proxy or WAF rewrites the response; the Url targets a non-Turso HTTP service; a server bug maps an error condition to a null 200 response.

Related errors


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