tursodatabase/turso · error · TursoException

Remote close returned unexpected response type: {result.Resp

Error message

Remote close returned unexpected response type: {result.Response?.Type}

What it means

During connection close, every result entry in the pipeline response must be an ok whose inner response type is "close". ValidateCloseResult threw because an ok entry carried a different inner response type, so the reply does not acknowledge the close request that was sent.

Source

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

        for (var i = 0; i < batch.StepResults.Count; i++)
        {
            var stepResult = batch.StepResults[i]
                             ?? throw new TursoException($"Remote batch did not return a result for step {i}.");
            statementResults.Add(stepResult);
        }

        return statementResults;
    }

    private static void ValidateCloseResult(RemotePipelineResponse response)
    {
        foreach (var result in response.Results)
        {
            switch (result.Type)
            {
                case "ok":
                    if (result.Response?.Type is not "close")
                        throw new TursoException($"Remote close returned unexpected response type: {result.Response?.Type}");
                    break;

                case "error":
                    throw CreateRemoteError(result.Error);

                default:
                    throw new TursoException($"Remote close returned unexpected result type: {result.Type}");
            }
        }
    }

    private static TursoException CreateRemoteError(RemoteError? error)
    {
        if (error is null)
            return new TursoRemoteSqlException("Remote SQL execution failed.");

        return string.IsNullOrWhiteSpace(error.Code)
            ? new TursoRemoteSqlException($"Remote SQL execution failed: {error.Message}")

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Check whether the exception occurs during CloseAsync/Dispose and whether the session had been idle long enough to expire server side.
  2. Align server and Turso.Data binding versions.
  3. If sessions expire quickly, lower idle time between requests or reopen connections per unit of work so close happens on a live session.
  4. Remove response-rewriting intermediaries.
  5. Report with the raw envelope if current versions still send a non-close ack.

Example fix

// before -- a close failure during disposal can mask the real work result
await using var conn = new TursoConnection(cs);
await conn.OpenAsync();
await DoWorkAsync(conn); // exception at Dispose if close ack is malformed

// after -- separate work from close so close failures are visible and non-fatal
var conn = new TursoConnection(cs);
try
{
    await conn.OpenAsync();
    await DoWorkAsync(conn);
}
finally
{
    try { await conn.CloseAsync(); }
    catch (TursoException ex) { logger.LogWarning(ex, "Session close failed; server will reap it"); }
}
Defensive patterns

Strategy: try-catch

Try / catch

try { await conn.CloseAsync(); }
catch (TursoException ex) when (ex.Message.Contains("Remote close"))
{
    // the work already committed; a malformed close ack is a server/protocol issue
    logger.LogWarning(ex, "Session close ack malformed; server will reap the session");
}

Prevention

When it happens

Trigger: Closing or disposing a remote TursoConnection that has an open session (a baton): CloseAsync posts a close request and the server's ok entry has response.type != "close". This can surface from 'await using var conn = ...' disposal, which surprises developers because the exception fires at Dispose time.

Common situations: Server version drift in close semantics; sessions already invalidated server side (idle expiry) answered with a different payload; gateways rewriting responses; custom servers that skip close acknowledgment.

Related errors


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