{"record":{"id":"d554a85d1ee25e4d","repo":"apache/beam","slug":"bundle-is-not-in-a-finalizable-state-for-s","errorCode":null,"errorMessage":"Bundle is not in a finalizable state for %s","messagePattern":"Bundle is not in a finalizable state for (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/runners/worker/sdk_worker.py","lineNumber":802,"sourceCode":"    # type: (...) -> beam_fn_api_pb2.InstructionResponse\n    try:\n      processor = self.bundle_processor_cache.lookup(request.instruction_id)\n    except RuntimeError:\n      return beam_fn_api_pb2.InstructionResponse(\n          instruction_id=instruction_id, error=traceback.format_exc())\n    if processor:\n      try:\n        finalize_response = processor.finalize_bundle()\n        self.bundle_processor_cache.release(request.instruction_id)\n        return beam_fn_api_pb2.InstructionResponse(\n            instruction_id=instruction_id, finalize_bundle=finalize_response)\n      except Exception as e:\n        self.bundle_processor_cache.discard(request.instruction_id, e)\n        raise\n    # We can reach this state if there was an erroneous request to finalize\n    # the bundle while it is being initialized or has already been finalized\n    # and released.\n    raise RuntimeError(\n        'Bundle is not in a finalizable state for %s' % instruction_id)\n\n  @contextlib.contextmanager\n  def maybe_profile(self, instruction_id):\n    # type: (str) -> Iterator[None]\n    if self.profiler_factory:\n      profiler = self.profiler_factory(instruction_id)\n      if profiler:\n        with profiler:\n          yield\n      else:\n        yield\n    else:\n      yield\n\n\nclass StateHandler(metaclass=abc.ABCMeta):\n  \"\"\"An abstract object representing a ``StateHandler``.\"\"\"","sourceCodeStart":784,"sourceCodeEnd":820,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/runners/worker/sdk_worker.py#L784-L820","documentation":" SdkWorkerBundle finalization requires the bundle to be in a state where it can be finalized. If bundle_processor_cache.lookup returns None/raises or the processor for instruction_id is not in a finalizable state, RuntimeError 'Bundle is not in a finalizable state' is raised. Beam notes this happens when finalize arrives while the bundle is still initializing or after it was already finalized and released.","triggerScenarios":" A finalize_bundle(request) arrives for an instruction id whose BundleProcessor was already finalized and released, or is still being set up, or was discarded due to failure.","commonSituations":" Runner retry logic sending finalize twice for one bundle; race between bundle completion and finalize control messages; runner/harness version skew in the bundle lifecycle protocol; very short-lived bundles where finalize is delayed past release.","solutions":["Ensure the runner sends finalize exactly once per successful process_bundle.","Fix runner-side retries to use a new instruction id rather than re-finalizing a completed one.","Align runner and SDK harness versions so lifecycle state transitions match.","If caused by concurrent control messages, serialize finalize handling on the runner or upgrade Beam to a version with the relevant race fixed."],"exampleFix":"# before\nawait send(ProcessBundle(instruction_id=i))\nawait send(FinalizeBundle(instruction_id=i))\nawait send(FinalizeBundle(instruction_id=i))  # retry duplicates finalize\n\n# after\nawait send(ProcessBundle(instruction_id=i))\nif not finalized_ids.add(i):\n    await send(FinalizeBundle(instruction_id=i))  # finalize only once","handlingStrategy":"validation","validationCode":"if instruction_id in finalized_instruction_ids:\n    raise ValueError(f'{instruction_id} already finalized; finalize must be sent once')","typeGuard":null,"tryCatchPattern":"try:\n    finalize_bundle(request)\nexcept RuntimeError as e:\n    if 'not in a finalizable state' in str(e):\n        log.warning('duplicate/late finalize for %s', request.instruction_id)\n        return  # treat as idempotent no-op if the bundle already completed\n    raise","preventionTips":["Send FinalizeBundle exactly once per successful ProcessBundle.","Use fresh instruction ids for retries instead of re-finalizing.","Keep runner/harness versions aligned so lifecycle transitions agree."],"tags":["apache-beam","sdk-worker","lifecycle"],"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"}