apache/beam · error · java.lang.IllegalStateException

Non PCollection PValue that expands into itself

Error message

Non PCollection PValue that expands into itself <value>

What it means

During full expansion of a PValue's outputs, a non-PCollection PValue whose expand() returns exactly itself is rejected with IllegalStateException. Such a value would recurse infinitely when flattened — an internal pipeline-construction invariant violation.

Solutions

  1. Fix the custom PValue's expand() to return the actual underlying PCollection(s), never itself.
  2. If the value is genuinely a PCollection, make its expand() map to itself as a PCollection (allowed) rather than as a non-PCollection PValue.
  3. Check that composite transforms return PCollections, not wrapper PValues, in their output maps.
  4. Report to Beam dev list if it occurs with built-in transforms (likely a bug).

Example fix

// before
@Override
public Map<TupleTag<?>, PValue> expand() {
  return Collections.singletonMap(tag, this); // expands into itself, non-PCollection
}
// after
@Override
public Map<TupleTag<?>, PValue> expand() {
  return Collections.singletonMap(tag, underlyingPCollection);
}
Defensive patterns

Strategy: try-catch

Validate before calling

Map<TupleTag<?>, PValue> exp = value.expand();
if (exp.size() == 1 && exp.values().iterator().next() == value && !(value instanceof PCollection)) {
  throw new IllegalStateException("expand() must return underlying PCollections, not itself");
}

Type guard

static boolean expandsToSelf(PValue v) {
  Map<TupleTag<?>, PValue> e = v.expand();
  return e.size() == 1 && Iterables.getOnlyElement(e.values()).equals(v);
}

Try / catch

try {
  result = PValues.expandOutput(...);
} catch (IllegalStateException e) {
  throw new IllegalArgumentException("Custom PValue expands into itself; fix expand()", e);
}

Prevention

When it happens

Trigger: A custom PValue/PTransform whose expand() returns a map containing the value itself as its only expansion, then is passed through PValues.expandOutput/expandInput/expandValue.

Common situations: Custom composite PTransform implementations returning this/wrong field from expand(); a PValueBase subclass that forgot to expand into its actual underlying PCollections.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/values/PValues.java:77

    Map<TupleTag<?>, PCollection<?>> result = new LinkedHashMap<>();
    for (Map.Entry<TupleTag<?>, PValue> pvalue : partiallyExpanded.entrySet()) {
      if (pvalue.getValue() instanceof PCollection) {
        PCollection<?> previous = result.put(pvalue.getKey(), (PCollection<?>) pvalue.getValue());
        if (previous != null) {
          throw new IllegalArgumentException(
              String.format(
                  "Found conflicting %ss in flattened expansion of %s: %s maps to %s and %s",
                  partiallyExpanded,
                  TupleTag.class.getSimpleName(),
                  pvalue.getKey(),
                  previous,
                  pvalue.getValue()));
        }
      } else {
        if (pvalue.getValue().expand().size() == 1
            && Iterables.getOnlyElement(pvalue.getValue().expand().values())
                .equals(pvalue.getValue())) {
          throw new IllegalStateException(
              String.format(
                  "Non %s %s that expands into itself %s",
                  PCollection.class.getSimpleName(),
                  PValue.class.getSimpleName(),
                  pvalue.getValue()));
        }
        /* At this point we know it is a PCollectionView or some internal hacked PValue. To be
        liberal, we
        allow it to expand into any number of PCollections, but do not allow structures that
        require
        further recursion. */
        for (Map.Entry<TupleTag<?>, PValue> valueComponent :
            pvalue.getValue().expand().entrySet()) {
          if (!(valueComponent.getValue() instanceof PCollection)) {
            throw new IllegalStateException(
                String.format(
                    "A %s contained in %s expanded to a non-%s: %s",
                    PValue.class.getSimpleName(),

View on GitHub (pinned to 12126d8942)