apache/beam · error · TypeError

In , tag is not a string

Error message

In %s, tag %s is not a string

What it means

When a DoFn emits a TaggedOutput (for multi-output ParDo via with_outputs), Beam's _handle_tagged_output unwraps it and validates the tag. The tag must be a str because it indexes the tagged_receivers dict for the declared output PCollection. A non-string tag means Beam cannot route the element to the correct output and raises TypeError.

Solutions

  1. Convert the tag to a string: `TaggedOutput(str(tag), value)`.
  2. Ensure the tag matches one of the outputs declared in `beam.DoFn(...).with_outputs('main', 'other')`.
  3. Add a validation/assertion in the DoFn before constructing the TaggedOutput.

Example fix

// before
yield TaggedOutput(error_code, element)  # error_code is an int
// after
yield TaggedOutput(str(error_code), element)
Defensive patterns

Strategy: validation

Validate before calling

assert isinstance(tag, str), f'TaggedOutput tag must be str, got {type(tag).__name__}'

Type guard

def is_str_tag(tag):
    return isinstance(tag, str)

Try / catch

try:
    out = pipeline_result.wait_until_finish()
except TypeError as e:
    if 'tag' in str(e) and 'not a string' in str(e):
        log.error('TaggedOutput tag must be a string matching with_outputs(): %s', e)
    raise

Prevention

When it happens

Trigger: Yielding `TaggedOutput(1, value)` or `TaggedOutput(('a','b'), value)` from a DoFn decorated to produce tagged outputs, where the tag passed to TaggedOutput is not a str.

Common situations: Passing an integer enum or tuple as a tag; forgetting that with_outputs() tags declared as strings must match exactly; dynamic tag construction using non-string values.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at sdks/python/apache_beam/runners/common.py:1823

        windowed_value.windows *= len(windowed_input_element.windows)
      return windowed_value

    elif isinstance(result, TimestampedValue):
      assign_context = WindowFn.AssignContext(result.timestamp, result.value)
      windowed_value = WindowedValue(
          result.value, result.timestamp, self.window_fn.assign(assign_context))
      if len(windowed_input_element.windows) != 1:
        windowed_value.windows *= len(windowed_input_element.windows)
      return windowed_value

    else:
      return windowed_input_element.with_value(result)

  def _handle_tagged_output(self, result):
    if isinstance(result, TaggedOutput):
      tag = result.tag
      if not isinstance(tag, str):
        raise TypeError('In %s, tag %s is not a string' % (self, tag))
      return tag, result.value
    return None, result

  def _write_value_to_tag(self, tag, windowed_value, watermark_estimator):
    if watermark_estimator is not None:
      watermark_estimator.observe_timestamp(windowed_value.timestamp)

    if tag is None:
      self.main_receivers.receive(windowed_value)
    else:
      self.tagged_receivers[tag].receive(windowed_value)

  def _write_batch_to_tag(self, tag, windowed_batch, watermark_estimator):
    if watermark_estimator is not None:
      for timestamp in windowed_batch.timestamps:
        watermark_estimator.observe_timestamp(timestamp)

    if tag is None:

View on GitHub (pinned to 12126d8942)