apache/beam · error · ValueError

Steps must be an iterable, not a string

Error message

Steps must be an iterable, not a string

What it means

MetricsFilter.with_steps guards against the common mistake of passing a single step name string where an iterable of names is expected; without the check, the string would be iterated character by character and silently match nothing.

Source

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

    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])

  def with_steps(self, steps: Iterable[str]) -> 'MetricsFilter':
    if isinstance(steps, str):
      raise ValueError('Steps must be an iterable, not a string')

    self._steps.update(steps)
    return self


class Lineage:
  """Standard collection of metrics used to record source and sinks information
  for lineage tracking."""

  LINEAGE_NAMESPACE = "lineage"
  SOURCE = "sources_v2"
  SINK = "sinks_v2"

  _METRICS = {
      SOURCE: Metrics.bounded_trie(LINEAGE_NAMESPACE, SOURCE),
      SINK: Metrics.bounded_trie(LINEAGE_NAMESPACE, SINK)
  }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Wrap the step in a list: with_steps(['Generate/Process']).
  2. Use with_step('Generate/Process') for a single step.
  3. Validate the input is a non-str iterable before calling.

Example fix

- MetricsFilter().with_steps('Process')
+ MetricsFilter().with_steps(['Process'])
Defensive patterns

Strategy: validation

Validate before calling

steps = [step] if isinstance(step, str) else list(step)
filter_ = MetricsFilter().with_steps(steps)

Type guard

def as_step_list(steps):
    return [steps] if isinstance(steps, str) else list(steps)

Try / catch

try:
    f = MetricsFilter().with_steps(steps)
except ValueError:
    f = MetricsFilter().with_steps([steps])

Prevention

When it happens

Trigger: MetricsFilter().with_steps('Generate/Process') instead of with_steps(['Generate/Process']); triggered directly or via with_step misuse.

Common situations: MetricResults.query() with a filter built from a single step name pulled from config or a variable.

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/0a981d6c9d754d85. Report an issue: GitHub.