apache/beam · error · ValueError

Names must be a collection, not a string

Error message

Names must be a collection, not a string

What it means

MetricsFilter.with_names() requires an iterable of metric names; passing a single string raises ValueError because strings are themselves iterable and would be expanded into individual characters. This check exists to catch that classic mistake.

Source

Thrown at sdks/python/apache_beam/metrics/metric.py:362

  @property
  def names(self) -> frozenset[str]:
    return frozenset(self._names)

  @property
  def namespaces(self) -> frozenset[str]:
    return frozenset(self._namespaces)

  def with_metric(self, metric: 'Metric') -> 'MetricsFilter':
    name = metric.metric_name.name or ''
    namespace = metric.metric_name.namespace or ''
    return self.with_name(name).with_namespace(namespace)

  def with_name(self, name: str) -> 'MetricsFilter':
    return self.with_names([name])

  def with_names(self, names: Iterable[str]) -> 'MetricsFilter':
    if isinstance(names, str):
      raise ValueError('Names must be a collection, not a string')

    self._names.update(names)
    return self

  def with_namespace(self, namespace: Union[type, str]) -> 'MetricsFilter':
    return self.with_namespaces([namespace])

  def with_namespaces(
      self, namespaces: Iterable[Union[type, str]]) -> 'MetricsFilter':
    if isinstance(namespaces, str):
      raise ValueError('Namespaces must be an iterable, not a string')

    self._namespaces.update([Metrics.get_namespace(ns) for ns in namespaces])
    return self

  def with_step(self, step: str) -> 'MetricsFilter':
    return self.with_steps([step])

View on GitHub (pinned to 12126d8942)

Solutions

  1. Wrap the name in a list: with_names(['my_metric']).
  2. Use with_name('my_metric') for a single name — it wraps automatically.
  3. Pass a set/tuple/generator of names if filtering multiple.

Example fix

- MetricsFilter().with_names('my_metric')
+ MetricsFilter().with_names(['my_metric'])
# or for a single name
+ MetricsFilter().with_name('my_metric')
Defensive patterns

Strategy: validation

Validate before calling

names = [name] if isinstance(name, str) else list(name)
filter_ = MetricsFilter().with_names(names)

Type guard

def as_name_list(names):
    return [names] if isinstance(names, str) else list(names)

Try / catch

try:
    f = MetricsFilter().with_names(names)
except ValueError:
    f = MetricsFilter().with_names([names])

Prevention

When it happens

Trigger: MetricsFilter().with_names('my_metric') — a bare str instead of a list/set of names; also via with_name only when code calls with_names directly with a string.

Common situations: Filtering metrics when querying MetricResults and wrapping a single name without a list; refactoring from with_name to with_names and forgetting to wrap in a list.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/213a254a3fc5aa88. Report an issue: GitHub.