{"record":{"id":"213a254a3fc5aa88","repo":"apache/beam","slug":"names-must-be-a-collection-not-a-string","errorCode":null,"errorMessage":"Names must be a collection, not a string","messagePattern":"Names must be a collection, not a string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/metrics/metric.py","lineNumber":362,"sourceCode":"  @property\n  def names(self) -> frozenset[str]:\n    return frozenset(self._names)\n\n  @property\n  def namespaces(self) -> frozenset[str]:\n    return frozenset(self._namespaces)\n\n  def with_metric(self, metric: 'Metric') -> 'MetricsFilter':\n    name = metric.metric_name.name or ''\n    namespace = metric.metric_name.namespace or ''\n    return self.with_name(name).with_namespace(namespace)\n\n  def with_name(self, name: str) -> 'MetricsFilter':\n    return self.with_names([name])\n\n  def with_names(self, names: Iterable[str]) -> 'MetricsFilter':\n    if isinstance(names, str):\n      raise ValueError('Names must be a collection, not a string')\n\n    self._names.update(names)\n    return self\n\n  def with_namespace(self, namespace: Union[type, str]) -> 'MetricsFilter':\n    return self.with_namespaces([namespace])\n\n  def with_namespaces(\n      self, namespaces: Iterable[Union[type, str]]) -> 'MetricsFilter':\n    if isinstance(namespaces, str):\n      raise ValueError('Namespaces must be an iterable, not a string')\n\n    self._namespaces.update([Metrics.get_namespace(ns) for ns in namespaces])\n    return self\n\n  def with_step(self, step: str) -> 'MetricsFilter':\n    return self.with_steps([step])\n","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/metrics/metric.py#L344-L380","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the name in a list: with_names(['my_metric']).","Use with_name('my_metric') for a single name — it wraps automatically.","Pass a set/tuple/generator of names if filtering multiple."],"exampleFix":"- MetricsFilter().with_names('my_metric')\n+ MetricsFilter().with_names(['my_metric'])\n# or for a single name\n+ MetricsFilter().with_name('my_metric')","handlingStrategy":"validation","validationCode":"names = [name] if isinstance(name, str) else list(name)\nfilter_ = MetricsFilter().with_names(names)","typeGuard":"def as_name_list(names):\n    return [names] if isinstance(names, str) else list(names)","tryCatchPattern":"try:\n    f = MetricsFilter().with_names(names)\nexcept ValueError:\n    f = MetricsFilter().with_names([names])","preventionTips":["Use with_name() for a single name, with_names() for collections.","Never pass bare strings to plural-setter methods.","Centralize filter construction in a helper that normalizes inputs."],"tags":["python","apache-beam","metrics-filter","argument-validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}