#7645·flower

aggregate() returns NaN weights instead of raising when all results report num_examples=0

Author: Atishyy27Created Jul 19, 2026Updated Aug 6, 2026

Describe the bug

aggregate() in framework/py/flwr/server/strategy/aggregate.py divides by the total number of examples without checking for zero. If every result in a round reports num_examples=0, the division produces NaN for every parameter and returns it as the aggregated model. There is no exception, only a numpy RuntimeWarning, so the NaN model is redistributed to clients and the run continues.

The if not results guards upstream do not catch this: the results list is non empty, the counts inside it are zero.

Steps/Code to Reproduce

python
import numpy as np
from flwr.server.strategy.aggregate import aggregate, weighted_loss_avg

results = [([np.array([1.0, 2.0])], 0), ([np.array([3.0, 4.0])], 0)]
print(aggregate(results))          # -> [array([nan, nan])], no exception

weighted_loss_avg([(0, 0.5), (0, 0.7)])   # -> raises ZeroDivisionError

Output:

aggregate() RETURNED (no exception): [array([nan, nan])]
contains NaN: True
warning: RuntimeWarning

weighted_loss_avg RAISED: ZeroDivisionError - float division by zero

Expected Results

aggregate() should fail loudly, like the other aggregation paths already do. weighted_loss_avg and aggregate_inplace both raise ZeroDivisionError on the same all zero input, so today the three entry points disagree and only aggregate() corrupts silently.

Actual Results

A NaN model is returned and becomes the new global model. Training can continue for many rounds before anyone notices.

Additional context

Happy to send a PR, i have a fix and a regression test ready.