apache/beam · error · ValueError

Flags must be an iterable of of strings, not a single…

Error message

Flags must be an iterable of of strings, not a single string.

What it means

PipelineOptions.__init__ accepts flags as an iterable of strings; a single str technically passes Iterable[str] but iterates as characters, so it is explicitly rejected. The library raises ValueError to prevent almost-certainly unintended behavior.

Solutions

  1. Wrap the flags in a list: PipelineOptions(['--runner=DirectRunner']).
  2. If starting from a single string, split it: shlex.split(flags_str).
  3. Fix config parsing to always produce a list of strings.

Example fix

# before
options = PipelineOptions('--runner=DataflowRunner')
# after
import shlex
options = PipelineOptions(shlex.split('--runner=DataflowRunner'))
Defensive patterns

Strategy: type-guard

Validate before calling

if isinstance(flags, str):
    flags = shlex.split(flags)

Type guard

def is_flag_list(v) -> bool:
    return isinstance(v, (list, tuple)) and all(isinstance(x, str) for x in v)

Try / catch

try:
    opts = PipelineOptions(flags)
except ValueError as e:
    opts = PipelineOptions(shlex.split(flags))

Prevention

When it happens

Trigger: PipelineOptions('--runner=DirectRunner') — passing one flag as a bare string instead of a list.

Common situations: Hard-coding a single CLI flag in scripts, or building options from a string read from a config file without splitting it.

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/98d1770fc794c11e. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/options/pipeline_options.py:369

                flag names.
                Option names: These are defined as dest in the
                parser.add_argument() for each flag. Passing flags
                like {no_use_public_ips: True}, for which the dest is
                defined to a different flag name in the parser,
                would be discarded. Instead, pass the dest of
                the flag (dest of no_use_public_ips is use_public_ips).
    """
    # Initializing logging configuration in case the user did not set it up.
    logging.basicConfig()

    # self._flags stores a list of not yet parsed arguments, typically,
    # command-line flags. This list is shared across different views.
    # See: view_as().
    if isinstance(flags, str):
      # Unfortunately a single str passes the Iterable[str] test, as it is
      # an iterable of single characters.  This is almost certainly not the
      # intent...
      raise ValueError(
          "Flags must be an iterable of of strings, not a single string.")
    self._flags = flags

    # Build parser that will parse options recognized by the [sub]class of
    # PipelineOptions whose object is being instantiated.
    parser = _BeamArgumentParser(allow_abbrev=False)
    for cls in type(self).mro():
      if cls == PipelineOptions:
        break
      elif '_add_argparse_args' in cls.__dict__:
        cls._add_argparse_args(parser)  # type: ignore

    # The _visible_options attribute will contain options that were recognized
    # by the parser.
    self._visible_options, _ = parser.parse_known_args(flags)

    # self._all_options is initialized with overrides to flag values,
    # provided in kwargs, and will store key-value pairs for options recognized

View on GitHub (pinned to 12126d8942)