apache/beam · error · AbandonedNodeException

The pipeline contains abandoned PTransform(s).

Error message

The pipeline contains abandoned PTransform(s).

What it means

Same check as the abandoned PAssert case: if dangling pipeline nodes exist but they are not PAssert nodes, TestPipeline throws AbandonedNodeException stating the pipeline contains abandoned PTransform(s) — transforms present in the graph that the run never visited.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/testing/TestPipeline.java:239

      final List<TransformHierarchy.Node> runVisitedNodes =
          checkStateNotNull(
              this.runVisitedNodes,
              "Internal error: non-empty pipeline has been visited but still no runVisitedNodes");
      final List<TransformHierarchy.Node> pipelineNodes = recordPipelineNodes(pipeline);
      if (runVisitedNodes.equals(pipelineNodes)) {
        return;
      }

      final boolean hasDanglingPAssert =
          pipelineNodes.stream()
              .filter(Predicates.not(Predicates.in(runVisitedNodes)))
              .anyMatch(isPAssertNode);

      if (hasDanglingPAssert) {
        throw new AbandonedNodeException("The pipeline contains abandoned PAssert(s).");
      } else {
        throw new AbandonedNodeException("The pipeline contains abandoned PTransform(s).");
      }
    }

    private boolean pipelineRunSucceeded() {
      return runVisitedNodes != null;
    }

    @Override
    protected void afterPipelineExecution() {
      runVisitedNodes = recordPipelineNodes(pipeline);
      super.afterPipelineExecution();
    }

    @Override
    protected void afterUserCodeFinished() {
      super.afterUserCodeFinished();
      verifyPipelineExecution();
    }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Build the complete pipeline graph before calling run(); do not mutate afterwards.
  2. Ensure each TestPipeline is used by exactly one test and one run (create a fresh TestPipeline per test).
  3. Check for runner-side pruning if transforms are legitimately unused — restructure so they are connected to a sink or assertion.

Example fix

// before
pipeline.run();
result.apply(Count.globally());
// after
result.apply(Count.globally());
pipeline.run().waitUntilFinish();
Defensive patterns

Strategy: validation

Validate before calling

if (pipelineFinished) { throw new IllegalStateException("Do not apply transforms to a finished pipeline"); }

Try / catch

try { pipeline.run().waitUntilFinish(); } catch (AbandonedNodeException e) { fail("Pipeline graph was mutated after run(): " + e.getMessage()); }

Prevention

When it happens

Trigger: PTransforms appended to the pipeline graph after run(), or transforms on branches the runner skipped/optimized away, detected by comparing pipelineNodes with runVisitedNodes.

Common situations: Mutating the pipeline after run() in test setup/teardown; constructing pipelines in multiple threads sharing one TestPipeline; applying transforms to a pipeline instance whose run() already completed.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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