apache/beam · critical
stage[ ] no parent ID for side input
Error message
stage[%v] no parent ID for side input %v
What it means
Before a stage with side inputs can run, Prism checks readiness by looking up each side input's parent PCollection in em.pcolParents. If a side input's PCollection has no registered parent stage, the pipeline graph is inconsistent — the side input cannot be evaluated because its producing stage is unknown. The runner panics naming the stage and the side input.
Solutions
- Check how the side input PCollection is produced; ensure it comes from a materializable transform (e.g. an explicit GBK or DoFn stage)
- Try materializing the side input with an intermediate identity transform or GBK so prism registers a parent stage
- Simplify or replace exotic side-input patterns (e.g. side inputs of side inputs) with supported ones
- Upgrade to the latest Beam version; prism side-input handling has improved over releases
- If it persists, file an issue with the pipeline graph — this indicates a prism translation bug
Example fix
// before: side input from a transform prism cannot parent
side := beam.SideInput{Input: viewOfUnmaterializedPColl}
// after: force materialization via a view over a GBK-backed PCollection
sideInput := beam.SideInput{Input: view.AsList(s, materializedPColl)} Defensive patterns
Strategy: fallback
Validate before calling
if _, ok := pcolParents[side.Global]; !ok {
return fmt.Errorf("side input %v has no producing stage registered", side.Global)
} Prevention
- Produce side inputs from standard, materializable transforms
- Avoid exotic side-input topologies (views over unmaterialized PCollections)
- Keep prism updated; side-input translation improves each release
When it happens
Trigger: em.pcolParents lacks an entry for side.Global when checking side-input readiness, i.e. the PCollection feeding the side input was never registered as the output of a stage — usually a pipeline-construction/translation bug or an unsupported side-input wiring pattern.
Common situations: Side inputs whose producing transform was fused away or not materialized, AsView/View.AsMap usages on PCollections that prism failed to link, cross-language pipeline fragments where the side input producer lives in another runner component.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- stage[ ] no parent for side input , with parent ID
- couldn't decode characteristic for variant
- error decoding append bag user state window key
- error decoding residual header:
- error decoding watermarks
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/867773ebc83bbd5a.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/runners/prism/internal/engine/elementmanager.go:2596
case isStatefulStage && len(ss.sides) == 0 && em.sawResidual.Load() && ss.hasBuildableDataLocked(upstreamW):
// A stateful stage processes pending data at whatever the current
// watermark is, so data alone makes it ready once something is self
// checkpointing. Side input readiness comes from the watermark, so
// stages that read one keep waiting.
case inputW == upstreamW && previousInputW == inputW:
// Otherwise, use the progression of watermark to determine the bundle readiness.
slog.Debug("bundleReady: unchanged upstream watermark",
slog.String("stage", ss.ID),
slog.Group("watermark",
slog.Any("upstream == input == previousInput", inputW)))
return mtime.MinTimestamp, false, ptimeEventsReady, injectedReady
}
ready := true
for _, side := range ss.sides {
pID, ok := em.pcolParents[side.Global]
if !ok {
panic(fmt.Sprintf("stage[%v] no parent ID for side input %v", ss.ID, side))
}
parent, ok := em.stages[pID]
if !ok {
panic(fmt.Sprintf("stage[%v] no parent for side input %v, with parent ID %v", ss.ID, side, pID))
}
ow := parent.OutputWatermark()
if upstreamW > ow {
ready = false
}
}
return upstreamW, ready, ptimeEventsReady, injectedReady
}
// processingTimeNow gives the current processing time for the runner.
func (em *ElementManager) processingTimeNow() (ret mtime.Time) {
if em.testStreamHandler != nil && !em.testStreamHandler.completed {
return em.testStreamHandler.Now()
}View on GitHub (pinned to 12126d8942)