{"record":{"id":"b68c994cd8ff6f28","repo":"tursodatabase/turso","slug":"remote-request-returned-an-empty-response","errorCode":null,"errorMessage":"Remote request returned an empty response.","messagePattern":"Remote request returned an empty response\\.","errorType":"exception","errorClass":"TursoException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data/TursoRemoteClient.cs","lineNumber":158,"sourceCode":"\n        if (_authToken is not null)\n            httpRequest.Headers.Authorization = new AuthenticationHeaderValue(\"Bearer\", _authToken);\n\n        using var response = await _httpClient\n            .SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, effectiveCancellationToken)\n            .ConfigureAwait(false);\n\n        var body = await response.Content.ReadAsStringAsync(effectiveCancellationToken).ConfigureAwait(false);\n        if (!response.IsSuccessStatusCode)\n        {\n            throw new TursoException(\n                $\"Remote request failed with HTTP {(int)response.StatusCode} {response.ReasonPhrase}: {body}\");\n        }\n\n        try\n        {\n            return JsonSerializer.Deserialize<RemotePipelineResponse>(body, JsonOptions)\n                   ?? throw new TursoException(\"Remote request returned an empty response.\");\n        }\n        catch (JsonException ex)\n        {\n            throw new TursoException($\"Unable to parse remote response: {ex.Message}\");\n        }\n    }\n\n    private static CancellationTokenSource? CreateTimeout(int commandTimeout, CancellationToken cancellationToken)\n    {\n        if (commandTimeout <= 0)\n            return null;\n\n        var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);\n        timeout.CancelAfter(TimeSpan.FromSeconds(commandTimeout));\n        return timeout;\n    }\n\n    private static void ValidateAuthTokenTransport(Uri endpoint, string? authToken)","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/dotnet/src/Turso.Data/TursoRemoteClient.cs#L140-L176","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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."],"exampleFix":"// before\nawait using var cmd = conn.CreateCommand();\ncmd.CommandText = \"SELECT count(*) FROM users\";\nvar count = await cmd.ExecuteScalarAsync();\n\n// after -- retry once on an empty remote response, reopening the session first\nawait using var cmd = conn.CreateCommand();\ncmd.CommandText = \"SELECT count(*) FROM users\";\nobject? count;\nfor (var attempt = 0; ; attempt++)\n{\n    try\n    {\n        count = await cmd.ExecuteScalarAsync();\n        break;\n    }\n    catch (TursoException) when (attempt == 0)\n    {\n        await conn.CloseAsync();\n        await conn.OpenAsync(); // fresh session/baton\n    }\n}","handlingStrategy":"retry","validationCode":"var uri = new Uri(builder.Url);\nusing var probe = new HttpClient();\nusing var resp = await probe.PostAsync(\n    new Uri(uri, \"/v2/pipeline\"),\n    new StringContent(\"{\\\"requests\\\":[]}\", Encoding.UTF8, \"application/json\"));\nvar body = await resp.Content.ReadAsStringAsync();\nif (!resp.IsSuccessStatusCode || body.Trim() is \"null\" or \"\")\n    throw new InvalidOperationException($\"{uri} does not speak the Turso pipeline protocol.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    result = await cmd.ExecuteScalarAsync(cancellationToken);\n}\ncatch (TursoException ex) when (ex.Message.Contains(\"empty response\"))\n{\n    // session may be wedged: dispose, reopen, retry once\n    await conn.CloseAsync();\n    conn.Dispose();\n    conn = new TursoConnection(cs);\n    await conn.OpenAsync(cancellationToken);\n    result = await cmd.ExecuteScalarAsync(cancellationToken);\n}","preventionTips":["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."],"tags":["turso","dotnet","remote","json","http","pipeline"],"backgroundTag":"empty-response-body","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}