apache/beam · error
unable to rewrite coder
Error message
unable to rewrite coder %v for timer %v for transform %v in stage %v: %w
What it means
Prism's buildDescriptor rewrites unknown coders on timer family declarations of a ParDo transform so the stage's bundle carries concrete coder bytes. When lpUnknownCoders fails to resolve or encode the timer family coder, this error wraps the underlying reason (unknown coder ID, missing component coder, unsupported URN) with the timer, transform and stage context. It means the pipeline's coder graph is incomplete or malformed for that timer.
Solutions
- Inspect the wrapped %w error to find which coder URN or component ID is unknown
- Verify the transform's timer family coder exists in components.coders of the submitted pipeline
- Regenerate the pipeline with a Beam SDK version compatible with the prism runner
- Check cross-language expansion output for coders not registered in the Go runner
Example fix
// before: timer coder references unknown id
v.TimerFamilyCoderId = "coder_missing"
// after: ensure the coder is registered in pipeline components before submit
pcoli.CoderId = registerKnownCoder(p, MyTimerType{}) // id resolvable via lpUnknownCoders Defensive patterns
Strategy: validation
Validate before calling
// Before submitting, verify timer family coder is registered
if _, ok := comps.GetCoders()[timerFamilyCoderID]; !ok {
return fmt.Errorf("timer family coder %q not registered", timerFamilyCoderID)
} Try / catch
if err := submitPipeline(ctx, p); err != nil {
var coderErr *CoderRewriteError
if errors.As(err, &coderErr) { log.Fatalf("coder rewrite failed: %v", coderErr) }
return err
} Prevention
- Register all custom coders with the SDK before building timers
- Keep Beam SDK and runner versions aligned
- Test pipelines with timers on a local runner before prism
When it happens
Trigger: A ParDo transform in the stage declares a timer family whose TimerFamilyCoderId references a coder not present/known in components, or lpUnknownCoders fails while substituting a portable coder for an unknown one.
Common situations: SDK-generated pipelines with custom coders the runner hasn't seen; cross-language pipelines where a Python/Java SDK emits timer coders Go prism can't resolve; hand-crafted or truncated pipeline protobufs; Beam SDK/runner version mismatches introducing new coder URNs.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- buildDescriptor: couldn't retrieve coder
- buildDescriptor: couldn't rewrite coder
- buildDescriptor: failed to handle coder on stage
- buildDescriptor: failed to handle coder on stage
- buildDescriptor: failed to handle coder on stage
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/ff851d3ac6cc8746.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/runners/prism/internal/stage.go:529
case *pipepb.StateSpec_ReadModifyWriteSpec:
rewriteCoder(&s.ReadModifyWriteSpec.CoderId)
}
if rewriteErr != nil {
return rewriteErr
}
}
for timerID, v := range pardo.GetTimerFamilySpecs() {
stg.hasTimers = append(stg.hasTimers, engine.StaticTimerID{TransformID: tid, TimerFamily: timerID})
if v.TimeDomain == pipepb.TimeDomain_PROCESSING_TIME {
if stg.processingTimeTimers == nil {
stg.processingTimeTimers = map[string]bool{}
}
stg.processingTimeTimers[timerID] = true
}
rewrite = true
newCid, err := lpUnknownCoders(v.GetTimerFamilyCoderId(), coders, comps.GetCoders())
if err != nil {
return fmt.Errorf("unable to rewrite coder %v for timer %v for transform %v in stage %v: %w", v.GetTimerFamilyCoderId(), timerID, tid, stg.ID, err)
}
v.TimerFamilyCoderId = newCid
}
if rewrite {
pyld, err := proto.MarshalOptions{}.Marshal(pardo)
if err != nil {
return fmt.Errorf("unable to encode ParDoPayload for %v in stage %v after rewrite", tid, stg.ID)
}
t.Spec.Payload = pyld
}
}
if len(transforms) == 0 {
return fmt.Errorf("buildDescriptor: invalid stage - no transforms at all %v", stg.ID)
}
// Start with outputs, since they're simple and uniform.
sink2Col := map[string]string{}
col2Coders := map[string]engine.PColInfo{}View on GitHub (pinned to 12126d8942)