{"record":{"id":"48e874a9c78d5940","repo":"apache/beam","slug":"trying-to-append-to-a-cleared-listbuffer","errorCode":null,"errorMessage":"Trying to append to a cleared ListBuffer.","messagePattern":"Trying to append to a cleared ListBuffer\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/runners/portability/fn_api_runner/execution.py","lineNumber":134,"sourceCode":"    pass\n\n\nclass ListBuffer:\n  \"\"\"Used to support parititioning of a list.\"\"\"\n  def __init__(self, coder_impl: Optional[CoderImpl]) -> None:\n    self._coder_impl = coder_impl or CoderImpl()\n    self._inputs: list[bytes] = []\n    self._grouped_output: Optional[list[list[bytes]]] = None\n    self.cleared = False\n\n  def copy(self) -> 'ListBuffer':\n    new = ListBuffer(self._coder_impl)\n    new._inputs = [v for v in self._inputs]\n    return new\n\n  def extend(self, extra: 'Buffer') -> None:\n    if self.cleared:\n      raise RuntimeError('Trying to append to a cleared ListBuffer.')\n    if self._grouped_output:\n      raise RuntimeError('ListBuffer append after read.')\n    assert isinstance(extra, ListBuffer)\n    self._inputs.extend(extra._inputs)\n\n  def append(self, element: bytes) -> None:\n    if self.cleared:\n      raise RuntimeError('Trying to append to a cleared ListBuffer.')\n    if self._grouped_output:\n      raise RuntimeError('ListBuffer append after read.')\n    self._inputs.append(element)\n\n  def partition(self, n: int) -> list[list[bytes]]:\n    if self.cleared:\n      raise RuntimeError('Trying to partition a cleared ListBuffer.')\n    if len(self._inputs) >= n or len(self._inputs) == 0:\n      return [self._inputs[k::n] for k in range(n)]\n    else:","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/runners/portability/fn_api_runner/execution.py#L116-L152","documentation":"ListBuffer.extend() refuses to append another Buffer's inputs once this buffer has been cleared via clear(). After clear(), _inputs is emptied and cleared=True marks the buffer dead; extending it would corrupt the bundle-buffer reuse protocol used by the fn_api_runner. A RuntimeError is raised to surface the lifecycle violation.","triggerScenarios":"Calling buffer.extend(other) after buffer.clear() has been called (cleared is True), typically when reusing a cached Buffer object across bundle executions instead of calling reset() first.","commonSituations":"Runner-side code caching ListBuffer instances between bundles and forgetting reset(); custom runner extensions that clear a buffer then continue writing; race between a clearing thread and an appending thread.","solutions":["Call reset() on the cleared buffer before extending it again, or create a fresh ListBuffer.","Restructure code so clear() is the last operation on a buffer (clear only when done).","Track buffer lifecycle explicitly (e.g. set reference to None after clear) to avoid accidental reuse."],"exampleFix":"# before\nbuf.clear()\nbuf.extend(other)  # RuntimeError\n# after\nbuf.clear()\nbuf.reset()\nbuf.extend(other)","handlingStrategy":"type-guard","validationCode":"if buffer.cleared:\n    buffer.reset()\nbuffer.extend(other)","typeGuard":"def is_writable(buf):\n    return not buf.cleared and not getattr(buf, '_grouped_output', None)","tryCatchPattern":"try:\n    buffer.extend(other)\nexcept RuntimeError:\n    buffer = ListBuffer(coder_impl)\n    buffer.extend(other)","preventionTips":["Never reuse a buffer after clear(); drop the reference instead.","Call reset() immediately after clear() when reuse is intended.","Keep a single owner responsible for buffer lifecycle transitions."],"tags":["apache-beam","fn-api-runner","buffer-lifecycle","runtime-error"],"backgroundTag":"invalid-state-transition","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}