tursodatabase/turso · error · InvalidOperationException

Encryption Cipher and Encryption Key are local database…

Error message

Encryption Cipher and Encryption Key are local database options and cannot be used with remote Turso URLs.

What it means

OpenRemote's third guard rejects local-only encryption options on remote URLs: Encryption Cipher and Encryption Key configure file-level encryption when opening a local database natively, and are meaningless over the wire, so supplying either (the check ORs a parsed cipher and any non-whitespace 'Encryption Key') with a remote Data Source throws InvalidOperationException at Open().

Solutions

  1. Remove 'Encryption Cipher' and 'Encryption Key' for remote connections — TLS secures the channel and the server handles encryption at rest
  2. Keep encryption options only on local file connection strings
  3. If you need a local encrypted database, use a file path Data Source where cipher plus key are honored

Example fix

// before
var cs = "Data Source=libsql://db.turso.io;Auth Token=...;Encryption Cipher=aes256;Encryption Key=a1b2...";

// after
var cs = "Data Source=libsql://db.turso.io;Auth Token=...";
Defensive patterns

Strategy: validation

Validate before calling

var opts = TursoConnectionOptions.Parse(cs);
if (opts.IsRemote && (opts.GetEncryptionCipher().HasValue || !string.IsNullOrWhiteSpace(opts["Encryption Key"])))
    throw new InvalidOperationException(
        "Remove encryption options: they only apply to local file databases.");

Type guard

static bool EncryptionOptionsMatchMode(string cs)
{
    var opts = TursoConnectionOptions.Parse(cs);
    var hasEncryption = opts.GetEncryptionCipher().HasValue
        || !string.IsNullOrWhiteSpace(opts["Encryption Key"]);
    return opts.IsRemote ? !hasEncryption : true;
}

Try / catch

try { conn.Open(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("Encryption Cipher and Encryption Key are local"))
{
    // strip Encryption Cipher/Encryption Key from the remote connection string and retry
}

Prevention

When it happens

Trigger: Open() with 'Data Source=libsql://...;Encryption Cipher=aes256;Encryption Key=<hex>'; setting only 'Encryption Key' without a cipher on a remote URL; secrets managers injecting encryption keys into all connection strings uniformly.

Common situations: Security-hardened config templates applied to every environment; migrating an encrypted local database to Turso Cloud while keeping the keys in the string; copying between local and remote connection strings in appsettings.

Related errors


AI-assisted analysis of tursodatabase/turso@6c72522679 (2026-09-06). Data as JSON: /api/errors/e97241a99cd0f797. Report an issue: GitHub.

Appendix: source

Thrown at bindings/dotnet/src/Turso.Data/TursoConnection.cs:473

            InvalidateRemoteSession();
        }
    }

    private void OpenRemote()
    {
        if (_connectionOptions.IsReplica)
        {
            OpenReplica();
            return;
        }

        if (_connectionOptions.SyncInterval > 0)
            throw new NotSupportedException("Sync Interval requires an embedded replica connection.");
        if (_connectionOptions.HasAdvancedReplicaOptions)
            throw new NotSupportedException("Advanced sync options require an embedded replica connection.");

        if (_connectionOptions.GetEncryptionCipher().HasValue || !string.IsNullOrWhiteSpace(_connectionOptions["Encryption Key"]))
            throw new InvalidOperationException("Encryption Cipher and Encryption Key are local database options and cannot be used with remote Turso URLs.");

        var handler = RemoteMessageHandlerFactory?.Invoke();
        _remoteClient = handler is null
            ? new TursoRemoteClient(_connectionOptions.GetRemoteUri(), _connectionOptions.AuthToken)
            : new TursoRemoteClient(
                new HttpClient(handler),
                _connectionOptions.GetRemoteUri(),
                _connectionOptions.AuthToken,
                disposeHttpClient: true);
    }

    private void OpenReplica()
    {
        var lease = TursoReplicaRegistry
            .AcquireAsync(
                CreateReplicaOptions(),
                _connectionOptions.Pooling,
                TimeSpan.FromSeconds(_connectionOptions.SyncInterval),

View on GitHub (pinned to 6c72522679)