dotnet/orleans · critical · InvalidOperationException

Session created from configuration 'CassandraClusteringOptio

Error message

Session created from configuration 'CassandraClusteringOptions' is null.

What it means

InvalidOperationException thrown inside InitializeMembershipTable when the user-supplied delegate CassandraClusteringOptions.CreateSessionAsync returns a null ISession. Orleans requires a valid Cassandra session; a null return indicates a misconfigured factory. The message names the options type so the misconfiguration is identifiable.

Source

Thrown at src/Cassandra/Orleans.Clustering.Cassandra/CassandraClusteringTable.cs:47

        IServiceProvider serviceProvider)
    {
        _clusterOptions = clusterOptions.Value;
        _options = options.Value;
        _identifier = $"{_clusterOptions.ServiceId}-{_clusterOptions.ClusterId}";
        _serviceProvider = serviceProvider;
        _ttlSeconds = _options.GetCassandraTtlSeconds(clusterMembershipOptions.Value);
    }

    private ISession Session => _session ?? throw new InvalidOperationException(NotInitializedMessage);

    private OrleansQueries Queries => _queries ?? throw new InvalidOperationException(NotInitializedMessage);

    async Task IMembershipTable.InitializeMembershipTable(bool tryInitTableVersion)
    {
        _session = await _options.CreateSessionAsync(_serviceProvider);
        if (_session is null)
        {
            throw new InvalidOperationException($"Session created from configuration '{nameof(CassandraClusteringOptions)}' is null.");
        }

        _queries = await OrleansQueries.CreateInstance(_session);

        await _queries.EnsureTableExistsAsync(_options.InitializeRetryMaxDelay, _ttlSeconds);

        if (tryInitTableVersion)
            await _queries.EnsureClusterVersionExistsAsync(_options.InitializeRetryMaxDelay, _identifier);
    }

    async Task IMembershipTable.DeleteMembershipTableEntries(string clusterId)
    {
        if (string.Compare(clusterId, _clusterOptions.ClusterId, StringComparison.InvariantCultureIgnoreCase) != 0)
        {
            throw new ArgumentException(
                $"Cluster id {clusterId} does not match CassandraClusteringTable value of '{_clusterOptions.ClusterId}'.",
                nameof(clusterId));
        }

View on GitHub (pinned to fca799fa70)

Solutions

  1. Provide a CreateSessionAsync that always returns a non-null ISession (Cluster.Builder().AddContactPoint(...).Build().Connect(keyspace)).
  2. Verify the session-creation dependencies (contact points, credentials, keyspace) are configured in CassandraClusteringOptions.
  3. Throw a descriptive exception from inside CreateSessionAsync when session creation genuinely fails, instead of returning null.

Example fix

// before
options.CreateSessionAsync = _ => Task.FromResult<ISession>(null!); // null -> this throw

// after
options.CreateSessionAsync = async sp =>
{
    var cfg = sp.GetRequiredService<IOptions<CassandraConfig>>().Value;
    var cluster = Cluster.Builder()
        .AddContactPoints(cfg.ContactPoints)
        .WithCredentials(cfg.User, cfg.Password)
        .Build();
    return await cluster.ConnectAsync(cfg.Keyspace);
};
Defensive patterns

Strategy: validation

Validate before calling

options.CreateSessionAsync = async sp =>
{
    var cfg = sp.GetRequiredService<IOptions<CassandraConfig>>().Value;
    var cluster = Cluster.Builder().AddContactPoints(cfg.ContactPoints)
        .WithCredentials(cfg.User, cfg.Password).Build();
    var session = await cluster.ConnectAsync(cfg.Keyspace);
    return session ?? throw new InvalidOperationException("session null");
};

Type guard

static async Task<ISession> NonNullSession(Func<Task<ISession?>> factory) =>
    await factory() ?? throw new InvalidOperationException("session factory returned null");

Try / catch

try { await table.InitializeMembershipTable(true); }
catch (InvalidOperationException ex) when (ex.Message.Contains("is null"))
{ logger.LogCritical(ex, "Cassandra session factory returned null"); throw; }

Prevention

When it happens

Trigger: CassandraClusteringOptions.CreateSessionAsync returns null. This delegate is responsible for building the ISession (cluster/builder); if it returns null, the table cannot proceed and initialization aborts before OrleansQueries is built.

Common situations: A custom CreateSessionAsync implementation with a missing return path (e.g. returns null on a not-found config), the default factory failing silently, or a DI scope where the session-creation service is not registered. Also seen when copy-pasting a sample that left the factory incomplete.

Related errors


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/20b31e58d5e345e0. Report an issue: GitHub.