{"record":{"id":"8cc20e2ea7392dac","repo":"apache/beam","slug":"plan-v-failed","errorCode":null,"errorMessage":"plan %v failed","messagePattern":"plan (.+?) failed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/runtime/exec/plan.go","lineNumber":228,"sourceCode":"func (p *Plan) Down(ctx context.Context) error {\n\t// Technically racy, but only one thread calls this method on the plan.\n\tif p.getStatus() == Down {\n\t\treturn nil // ok: already down\n\t}\n\tp.setStatus(Down)\n\n\tvar errs []error\n\tfor _, u := range p.units {\n\t\tif err := callNoPanic(ctx, u.Down); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\n\tswitch len(errs) {\n\tcase 0:\n\t\treturn nil\n\tcase 1:\n\t\treturn errors.Wrapf(errs[0], \"plan %v failed\", p.id)\n\tdefault:\n\t\treturn errors.Errorf(\"plan %v failed with multiple errors: %v\", p.id, errs)\n\t}\n}\n\nfunc (p *Plan) String() string {\n\tvar units []string\n\tfor i := len(p.units) - 1; i >= 0; i-- {\n\t\tu := p.units[i]\n\t\tunits = append(units, fmt.Sprintf(\"%v: %v\", u.ID(), u))\n\t}\n\treturn fmt.Sprintf(\"Plan[%v]:\\n%v\", p.ID(), strings.Join(units, \"\\n\"))\n}\n\n// PlanSnapshot contains system metrics for the current run of the plan.\ntype PlanSnapshot struct {\n\tSource ProgressReportSnapshot\n\tPCols  []PCollectionSnapshot","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/runtime/exec/plan.go#L210-L246","documentation":"Plan.Down() tears down all units and collects any errors from unit Down() calls. If exactly one unit failed teardown, that error is wrapped with \"plan %v failed\"; the plan's teardown therefore did not fully complete.","triggerScenarios":"Calling Plan.Down() when exactly one of the plan's units returns a non-nil error from its Down() method (e.g. a sink failing to close resources, a DoFn failing cleanup).","commonSituations":"Worker shutdown with a custom sink whose Close() fails (already-closed file, network flush failure); datasource failing to release a reader; resource leaks accumulating when the same error recurs at every bundle teardown.","solutions":["Inspect the wrapped error to identify which unit's Down() failed","Fix the failing unit's cleanup logic (handle double-close, tolerate already-released resources)","Make custom sink/DoFn Down() methods idempotent so repeated teardown doesn't error","Ensure process-level resources (files, connections) are closed even if Down() errors, to avoid leaks"],"exampleFix":"// before: Down fails on double-close\nfunc (s *mySink) Down(ctx context.Context) error {\n    return s.file.Close()\n}\n// after\nfunc (s *mySink) Down(ctx context.Context) error {\n    if s.file == nil {\n        return nil\n    }\n    err := s.file.Close()\n    s.file = nil\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Make unit cleanup safe before teardown\nfunc (s *mySink) Down(ctx context.Context) error {\n    if s.file == nil { return nil }\n    return s.file.Close()\n}","typeGuard":"func (s *mySink) isClosed() bool { return s.file == nil }","tryCatchPattern":"if err := plan.Down(); err != nil {\n    log.Printf(\"teardown error (check wrapped cause): %v\", errors.Unwrap(err))\n    // attempt process-level resource cleanup as fallback\n}","preventionTips":["Implement idempotent Down() in custom sinks and DoFns","Handle double-close of files/connections gracefully","Log cleanup errors instead of leaking resources when teardown partially fails","Test worker-shutdown paths with open sinks"],"tags":["go","apache-beam","execution-plan","teardown"],"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"}