dotnet/orleans · critical · InvalidOperationException

Session created from configuration 'CassandraClusteringOptio

Error message

Session created from configuration 'CassandraClusteringOptions' is null.

What it means

InvalidOperationException inside InitializeGatewayListProvider when CassandraClusteringOptions.CreateSessionAsync returns a null ISession. The provider cannot fetch gateways without a Cassandra session, so initialization aborts before EnsureTableExistsAsync runs. The message names CassandraClusteringOptions.

Source

Thrown at src/Cassandra/Orleans.Clustering.Cassandra/CassandraGatewayListProvider.cs:55

    {
        _identifier = $"{clusterOptions.Value.ServiceId}-{clusterOptions.Value.ClusterId}";
        _options = options.Value;
        _serviceProvider = serviceProvider;

        _maxStaleness = gatewayOptions.Value.GatewayListRefreshPeriod;
        _ttlSeconds = _options.GetCassandraTtlSeconds(clusterMembershipOptions.Value);
    }

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

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

    async Task IGatewayListProvider.InitializeGatewayListProvider()
    {
        _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);
    }
    
    async Task<IList<Uri>> IGatewayListProvider.GetGateways()
    {
        if (_cachedResult is not null && _cacheUntil > DateTime.UtcNow)
        {
            return [.. _cachedResult];
        }

        var rows = await Session.ExecuteAsync(await Queries.GatewaysQuery(_identifier, (int)SiloStatus.Active));
        var result = new List<Uri>();

        foreach (var row in rows)

View on GitHub (pinned to fca799fa70)

Solutions

  1. Implement CreateSessionAsync to always return a connected ISession (Cluster.Builder()...ConnectAsync(keyspace)).
  2. Validate CassandraClusteringOptions (contact points, keyspace, credentials) at client build time.
  3. Throw inside the factory instead of returning null when session creation genuinely fails.

Example fix

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

// after
options.CreateSessionAsync = async sp =>
    await Cluster.Builder().AddContactPoints(cfg.ContactPoints).Build().ConnectAsync(cfg.Keyspace);
Defensive patterns

Strategy: validation

Validate before calling

options.CreateSessionAsync = async sp =>
    await Cluster.Builder().AddContactPoints(cfg.ContactPoints).Build().ConnectAsync(cfg.Keyspace)
    is { } s ? s : 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 gw.InitializeGatewayListProvider(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("is null"))
{ logger.LogCritical(ex, "Cassandra session factory returned null"); throw; }

Prevention

When it happens

Trigger: CreateSessionAsync returns null for the gateway provider, identical root cause to error 349 but on the client-side gateway list provider.

Common situations: Incomplete or misconfigured CreateSessionAsync factory, missing contact points/credentials in options, or a custom factory with a code path that returns null on a fallback. Most often a copy-paste of a sample that was not filled in.

Related errors


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