Describe the bug
weighted_loss_avg() in framework/py/flwr/server/strategy/aggregate.py divides by the total number of evaluation examples without guarding against zero:
num_total_evaluation_examples = sum(num_examples for (num_examples, _) in results)
weighted_losses = [num_examples * loss for num_examples, loss in results]
return sum(weighted_losses) / num_total_evaluation_examples
If every client in a round reports num_examples=0 (a degenerate or empty local evaluation partition), this raises ZeroDivisionError: float division by zero and aborts the round.
The callers — FedAvg.aggregate_evaluate, FaultTolerantFedAvg.aggregate_evaluate, FedAvgAndroid.aggregate_evaluate — only guard if not results: return None, {}. That catches an empty result list, but not a non-empty list whose examples all sum to zero.
This is the same class as #7645 (aggregate() returning NaN weights on a zero total), just in the evaluation path — and here it hard-raises rather than silently producing NaN.
Steps to reproduce
from flwr.server.strategy.aggregate import weighted_loss_avg
weighted_loss_avg([(0, 2.0), (0, 3.0)])
# ZeroDivisionError: float division by zero
Expected behavior
A clear ValueError explaining that the total number of evaluation examples must be greater than zero, consistent with the guard proposed for aggregate() in #7646.
I have a fix and a regression test ready and will open a PR referencing this issue.
Describe the bug
weighted_loss_avg()inframework/py/flwr/server/strategy/aggregate.pydivides by the total number of evaluation examples without guarding against zero:If every client in a round reports
num_examples=0(a degenerate or empty local evaluation partition), this raisesZeroDivisionError: float division by zeroand aborts the round.The callers —
FedAvg.aggregate_evaluate,FaultTolerantFedAvg.aggregate_evaluate,FedAvgAndroid.aggregate_evaluate— only guardif not results: return None, {}. That catches an empty result list, but not a non-empty list whose examples all sum to zero.This is the same class as #7645 (
aggregate()returning NaN weights on a zero total), just in the evaluation path — and here it hard-raises rather than silently producing NaN.Steps to reproduce
Expected behavior
A clear
ValueErrorexplaining that the total number of evaluation examples must be greater than zero, consistent with the guard proposed foraggregate()in #7646.I have a fix and a regression test ready and will open a PR referencing this issue.