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
- 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.
- 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.
- Disable any edge middleware (bot protection, auth edge, WAF) that can answer 200 with an empty or null body on this route.
- If the failure is intermittent, treat it as transient: close and reopen the connection to get a fresh session, then retry the command once.
- 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
- Point Url directly at the database endpoint; verify /v2/pipeline with curl before wiring the connection string.
- Do not route database traffic through gateways or WAFs that can answer 200 with an empty or null body.
- Run a SELECT 1 smoke test through the real connection at application startup.
- Keep server and bindings versions in sync so both sides speak the same pipeline protocol.
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
- Unable to parse remote response: {ex.Message}
- Remote request returned no results.
- Remote request returned an empty ok response.
- Remote batch returned no results.
- Remote response {Type} did not include a result.
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/b68c994cd8ff6f28.
Report an issue: GitHub.