Skip to content

Polling data source throws NullReferenceException on every HTTP 304 (Not Modified) response #344

Description

@cda210

Summary

When using the server-side .NET SDK in polling mode, every poll that receives an HTTP 304 Not Modified response (i.e. flags haven't changed since the last poll) throws and logs a NullReferenceException. It's caught by the generic exception handler so it doesn't crash the process, but it's logged as a Warning on effectively every polling interval once the SDK has initialized and flags are stable, and it incorrectly marks the data source status as Interrupted even though nothing is actually wrong.

Root cause

In FeatureRequestor.GetAllDataAsync():

public async Task<DataSetWithHeaders> GetAllDataAsync()
{
    var res = await GetAsync(_allUri);
    if (res is null)
    {
        return null; // <-- returns a null DataSetWithHeaders reference on 304
    }
    ...
}

DataSetWithHeaders is a reference type (see IFeatureRequestor.cs), so on a 304 response GetAllDataAsync() returns a literal null, not an instance with a null DataSet property.

But in PollingDataSource.UpdateTaskAsync():

var dataAndHeaders = await _featureRequestor.GetAllDataAsync();
if (dataAndHeaders.DataSet is null)  // <-- NullReferenceException here when dataAndHeaders itself is null
{
    // This means it was cached, and alreadyInited was true
    _dataSourceUpdates.UpdateStatus(DataSourceState.Valid, null);
}

This dereferences dataAndHeaders.DataSet without first checking whether dataAndHeaders itself is null, throwing an NRE that's then caught by the generic catch (Exception ex) block further down, logged as:

Polling for feature flag updates failed: System.NullReferenceException: Object reference not set to an instance of an object.
   at LaunchDarkly.Sdk.Server.Internal.DataSources.PollingDataSource.UpdateTaskAsync()

...and reported via _dataSourceUpdates.UpdateStatus(DataSourceState.Interrupted, errorInfo) — even though a 304 is a perfectly healthy "no changes" response, not an interruption.

Impact

  • Every poll where flags haven't changed logs a spurious Warning (and, if the caller has enabled the SDK's Debug logging, a matching stack trace) that looks like a real failure but isn't.
  • DataSourceStatus is misreported as Interrupted instead of Valid for these no-op polls, which could affect consumers monitoring data source health via DataSourceStatusProvider.
  • Flag evaluation itself is not affected, since this occurs after the flag store already has valid cached data from a prior successful poll.

Suggested fix

var dataAndHeaders = await _featureRequestor.GetAllDataAsync();
if (dataAndHeaders is null || dataAndHeaders.DataSet is null)
{
    _dataSourceUpdates.UpdateStatus(DataSourceState.Valid, null);
}

Repro

  • SDK: LaunchDarkly.ServerSdk 8.16.0 (also confirmed present in current main of this repo as of 2026-09-03)
  • Configuration: Components.PollingDataSource(), any poll interval, running against the real LaunchDarkly polling endpoint (https://sdk.launchdarkly.com/sdk/latest-all)
  • Trigger: let the client poll at least twice without any flag changes in between. The second (and every subsequent) poll that gets a 304 will throw.

Environment

  • .NET 8, AWS Lambda
  • LaunchDarkly.ServerSdk 8.16.0

Happy to submit a PR with the one-line fix above if that's helpful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions