apache/beam · error · NotImplementedError

ToBytesCoder cannot be used for decoding.

Error message

ToBytesCoder cannot be used for decoding.

What it means

ToBytesCoder is a write-only convenience coder: its encode converts anything to bytes, but it intentionally does not implement decode and raises NotImplementedError('ToBytesCoder cannot be used for decoding.') whenever decoding is attempted. It exists as a default when no sink coder is specified and reading back is never expected.

Source

Thrown at sdks/python/apache_beam/coders/coders.py:435

  def is_deterministic(self):
    # type: () -> bool
    return True

  def to_type_hint(self):
    return str


Coder.register_structured_urn(common_urns.coders.STRING_UTF8.urn, StrUtf8Coder)


class ToBytesCoder(Coder):
  """A default string coder used if no sink coder is specified."""
  def encode(self, value):
    return value if isinstance(value, bytes) else str(value).encode('utf-8')

  def decode(self, _):
    raise NotImplementedError('ToBytesCoder cannot be used for decoding.')

  def is_deterministic(self):
    # type: () -> bool
    return True


# alias to the old class name for a courtesy to users who reference it
ToStringCoder = ToBytesCoder


class FastCoder(Coder):
  """Coder subclass used when a (faster) CoderImpl is supplied directly.

  The Coder class defines _create_impl in terms of encode() and decode();
  this class inverts that by defining encode() and decode() in terms of
  _create_impl().
  """
  def encode(self, value):

View on GitHub (pinned to 12126d8942)

Solutions

  1. Replace ToBytesCoder with a decodable coder such as coders.StrBytesCoder (or coders.BytesCoder for raw bytes)
  2. Keep ToBytesCoder only on write-only paths where values are never read back
  3. Decode at write time instead: store str/bytes elements and use a coder that round-trips

Example fix

// before
pcoll = pcoll | beam.Map(lambda x: str(x)) with coder ToBytesCoder() ... later decoded
// after
pcoll = pcoll | beam.Map(lambda x: str(x)) with coder coders.StrBytesCoder()
Defensive patterns

Strategy: try-catch

Validate before calling

if isinstance(coder, ToBytesCoder) and need_decoding:
    coder = coders.StrBytesCoder()

Type guard

def is_decodable_coder(coder):
    return not isinstance(coder, ToBytesCoder) and coder.is_deterministic() is not None

Try / catch

try:
    value = coder.decode(blob)
except NotImplementedError:
    value = blob.decode('utf-8')  # ToBytesCoder wrote str(x).encode('utf-8')

Prevention

When it happens

Trigger: Calling ToBytesCoder().decode(bytes) directly; pipelines or checkpoint mechanisms that try to read back data written with ToBytesCoder (checkpoint marks, side-input materialization, bag page values).

Common situations: Using ToBytesCoder as a general-purpose string coder because the name suggests 'to bytes', then a transform needs to decode; writing to sinks with the default coder and later reading that data in another step/job.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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