dotnet/orleans · error · InvalidOperationException

The configured Azure Table service client factory returned n

Error message

The configured Azure Table service client factory returned null.

What it means

Thrown during Initialize when the configured CreateClient factory delegate returns null after awaiting. The contract requires the factory to produce a non-null TableServiceClient; a null result indicates a broken factory and is rejected immediately.

Source

Thrown at src/Azure/Orleans.Journaling.AzureStorage/AzureTableJournalStorageProvider.cs:43

        _options = options.Value;
        var journalFormatKey = ValidateJournalFormatKey(managerOptions.Value.JournalFormatKey);
        ValidateJournalFormat(serviceProvider, journalFormatKey);
        _shared = new AzureTableJournalStorage.AzureTableJournalStorageShared(
            logger,
            options,
            _tableClientProvider,
            instruments ?? AzureTableJournalStorageInstruments.CreateForDirectConstruction(),
            journalFormatKey);
    }

    private async Task Initialize(CancellationToken cancellationToken)
    {
        var createClient = _options.CreateClient
            ?? throw new InvalidOperationException(
                $"No Azure Table service client was configured. Set {nameof(AzureTableJournalStorageOptions.TableServiceClient)} " +
                $"or call {nameof(AzureTableJournalStorageOptions.ConfigureTableServiceClient)}.");
        var client = await createClient(cancellationToken).ConfigureAwait(false)
            ?? throw new InvalidOperationException("The configured Azure Table service client factory returned null.");
        var table = client.GetTableClient(_options.TableName);
        await table.CreateIfNotExistsAsync(cancellationToken).ConfigureAwait(false);
        _tableClientProvider.SetTableClient(table);
    }

    public IJournalStorage CreateStorage(JournalId journalId)
    {
        if (journalId.IsDefault)
        {
            throw new ArgumentException("The journal id must not be the default value.", nameof(journalId));
        }

        return new AzureTableJournalStorage(_shared, journalId);
    }

    public async IAsyncEnumerable<JournalId> ListAsync(
        JournalId prefix = default,
        [EnumeratorCancellation] CancellationToken cancellationToken = default)

View on GitHub (pinned to fca799fa70)

Solutions

  1. Ensure the factory always returns a valid TableServiceClient instance; throw inside the factory if construction fails.
  2. Replace a null-returning factory with a simpler ConfigureTableServiceClient overload (connection string / Uri / credential).
  3. Add a null check inside the factory that throws a descriptive exception before returning.

Example fix

// before
options.ConfigureTableServiceClient(ct => Task.FromResult<TableServiceClient>(null!));
// after
options.ConfigureTableServiceClient(ct => Task.FromResult(new TableServiceClient(connectionString)));
Defensive patterns

Strategy: validation

Validate before calling

var client = await options.CreateClient!(ct);
if (client is null) throw new InvalidOperationException("Client factory returned null.");

Type guard

static bool FactoryReturnsNonNull(Func<CancellationToken, Task<TableServiceClient>> f, CancellationToken ct) => f(ct).GetAwaiter().GetResult() is not null;

Prevention

When it happens

Trigger: A custom ConfigureTableServiceClient callback returns null (e.g. ct => Task.FromResult<TableServiceClient>(null)) due to a failed internal client construction or a misplaced default.

Common situations: A factory that conditionally returns null on missing config instead of throwing; a dependency-injection resolver returning null; a refactor that dropped the 'new TableServiceClient(...)' expression.

Related errors


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