open-mmlab/mmdetection · error · TypeError

metric must be a list or a str.

Error message

metric must be a list or a str.

What it means

ReIDMetrics.__init__ only accepts metric as a list of strings or a single string; any other type (int, None, tuple, dict) raises this TypeError before any evaluation starts. It is a configuration-type guard on the constructor argument.

Source

Thrown at mmdet/evaluation/metrics/reid_metric.py:43

            If prefix is not provided in the argument, self.default_prefix
            will be used instead. Default: None
    """
    allowed_metrics = ['mAP', 'CMC']
    default_prefix: Optional[str] = 'reid-metric'

    def __init__(self,
                 metric: Union[str, Sequence[str]] = 'mAP',
                 metric_options: Optional[dict] = None,
                 collect_device: str = 'cpu',
                 prefix: Optional[str] = None) -> None:
        super().__init__(collect_device, prefix)

        if isinstance(metric, list):
            metrics = metric
        elif isinstance(metric, str):
            metrics = [metric]
        else:
            raise TypeError('metric must be a list or a str.')
        for metric in metrics:
            if metric not in self.allowed_metrics:
                raise KeyError(f'metric {metric} is not supported.')
        self.metrics = metrics

        self.metric_options = metric_options or dict(
            rank_list=[1, 5, 10, 20], max_rank=20)
        for rank in self.metric_options['rank_list']:
            assert 1 <= rank <= self.metric_options['max_rank']

    def process(self, data_batch: dict, data_samples: Sequence[dict]) -> None:
        """Process one batch of data samples and predictions.

        The processed results should be stored in ``self.results``, which will
        be used to compute the metrics when all batches have been processed.

        Args:
            data_batch (dict): A batch of data from the dataloader.

View on GitHub (pinned to cfd5d3a985)

Solutions

  1. Pass metric as a string, e.g. metric='mAP', or a list of strings, e.g. metric=['mAP','CMC']
  2. Check the config value resolves to str or list[str] before constructing the metric
  3. If loading configs dynamically, coerce: metric = list(metric) if isinstance(metric,(list,tuple)) else str(metric)

Example fix

# before
val_evaluator = dict(type='ReIDMetrics', metric=('mAP', 'CMC'))
# after
val_evaluator = dict(type='ReIDMetrics', metric=['mAP', 'CMC'])
Defensive patterns

Strategy: type-guard

Validate before calling

assert isinstance(metric, (str, list)) and all(isinstance(m, str) for m in (metric if isinstance(metric, list) else [metric])), 'metric must be str or list[str]'

Type guard

def is_valid_metric_arg(metric) -> bool:
    if isinstance(metric, str): return True
    return isinstance(metric, list) and all(isinstance(m, str) for m in metric)

Try / catch

try:
    m = ReIDMetrics(metric=metric)
except TypeError:
    metric = list(metric) if isinstance(metric, (list, tuple)) else [str(metric)]
    m = ReIDMetrics(metric=metric)

Prevention

When it happens

Trigger: Passing metric=None, metric=('mAP','CMC'), metric=1, or an unhashable/other object to ReIDMetrics; also a config file where the metric key resolves to a non-str value.

Common situations: YAML/Python config typo like metric: [mAP, CMC] being fine but metric: {mAP: CMC} failing; passing a generator or numpy array; older configs passing tuples.

Related errors


AI-assisted analysis of open-mmlab/mmdetection@cfd5d3a985 (2026-08-27). Data as JSON: /api/errors/345d8764c9d8be6a. Report an issue: GitHub.