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

MOTChallengeMetric.__init__ raises TypeError when the `metric` argument is neither a list nor a string. The evaluator accepts metric='mota' or metric=['mota','hota'] but any other type (dict, None, int) fails type validation before the per-metric check.

Source

Thrown at mmdet/evaluation/metrics/mot_challenge_metric.py:104

                 format_only: bool = False,
                 use_postprocess: bool = False,
                 postprocess_tracklet_cfg: Optional[List[dict]] = [],
                 collect_device: str = 'cpu',
                 prefix: Optional[str] = None) -> None:
        super().__init__(collect_device=collect_device, prefix=prefix)
        if trackeval is None:
            raise RuntimeError(
                'trackeval is not installed,'
                'please install it by: pip install'
                'git+https://github.com/JonathonLuiten/TrackEval.git'
                'trackeval need low version numpy, please install it'
                'by: pip install -U numpy==1.23.5')
        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.format_only = format_only
        if self.format_only:
            assert outfile_prefix is not None, 'outfile_prefix must be not'
            'None when format_only is True, otherwise the result files will'
            'be saved to a temp directory which will be cleaned up at the end.'
        self.use_postprocess = use_postprocess
        self.postprocess_tracklet_cfg = postprocess_tracklet_cfg.copy()
        self.postprocess_tracklet_methods = [
            TASK_UTILS.build(cfg) for cfg in self.postprocess_tracklet_cfg
        ]
        assert benchmark in self.allowed_benchmarks
        self.benchmark = benchmark
        self.track_iou_thr = track_iou_thr
        self.tmp_dir = tempfile.TemporaryDirectory()

View on GitHub (pinned to cfd5d3a985)

Solutions

  1. Set metric to a string or a list of strings, e.g. metric=['mota', 'idf1']
  2. Check the config variable producing metric for None values from templating/interpolation
  3. Prefer a list even for a single metric for consistency

Example fix

// before
val_evaluator=dict(type='MOTChallengeMetric', metric=None)
// after
val_evaluator=dict(type='MOTChallengeMetric', metric=['mota', 'idf1'])
Defensive patterns

Strategy: type-guard

Validate before calling

assert isinstance(metric, (str, list)), \
    f'metric must be str or list, got {type(metric).__name__}'
metrics = [metric] if isinstance(metric, str) else list(metric)

Type guard

def is_valid_mot_metric_arg(metric) -> bool:
    return isinstance(metric, str) or (isinstance(metric, list) and all(isinstance(m, str) for m in metric))

Try / catch

try:
    m = MOTChallengeMetric(metric=metric)
except TypeError as e:
    if 'list or a str' in str(e):
        metric = [metric] if isinstance(metric, str) else list(metric or [])
        m = MOTChallengeMetric(metric=metric)
    else:
        raise

Prevention

When it happens

Trigger: Passing val_evaluator=dict(type='MOTChallengeMetric', metric=None) or metric=('mota',) (tuple) or an int/dict as the metric key in the config.

Common situations: Config templating that injects None when a variable is undefined; using a tuple instead of a list in a Python-constructed config; YAML configs coercing an odd type for the metric field.

Related errors


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