{"record":{"id":"45a20e50c1b5df5b","repo":"gastownhall/beads","slug":"epic-s-has-d-open-child-issue-s-close-children","errorCode":null,"errorMessage":"epic %s has %d open child issue(s); close children first or use --force to override","messagePattern":"epic (.+?) has (.+?) open child issue\\(s\\); close children first or use --force to override","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/validation/issue.go","lineNumber":292,"sourceCode":"\t\t\t}\n\t\t}\n\t\treturn fmt.Errorf(\"issue %s has type %s, expected one of: %v\", id, issue.IssueType, allowed)\n\t}\n}\n\n// EpicHasOpenChildren validates that an epic does not have open children.\n// If the issue is an epic with open children, returns an error unless force is true.\n// Non-epic issues pass through without validation.\nfunc EpicHasOpenChildren(force bool, openChildCount int) IssueValidator {\n\treturn func(id string, issue *types.Issue) error {\n\t\tif issue == nil || force {\n\t\t\treturn nil\n\t\t}\n\t\tif issue.IssueType != types.TypeEpic {\n\t\t\treturn nil\n\t\t}\n\t\tif openChildCount > 0 {\n\t\t\treturn fmt.Errorf(\"epic %s has %d open child issue(s); close children first or use --force to override\", id, openChildCount)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// forUpdate returns a validator chain for update operations.\n// Validates: issue exists and is not a template.\nfunc forUpdate() IssueValidator {\n\treturn Chain(\n\t\tExists(),\n\t\tNotTemplate(),\n\t)\n}\n\n// forClose returns a validator chain for close operations.\n// Validates: issue exists, is not a template, and is not pinned (unless force).\nfunc forClose(force bool) IssueValidator {\n\treturn Chain(","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/validation/issue.go#L274-L310","documentation":"This error is returned by the EpicHasOpenChildren validator when attempting to close (or otherwise finalize) an epic that still has open child issues. The library refuses to close an epic whose work items are incomplete, and reports the count of open children. The user can either close the children first or explicitly override with a force flag.","triggerScenarios":"Closing or validating an issue where issue.IssueType == types.TypeEpic and openChildCount > 0 and force is false. Non-epic issues pass through without validation; force=true bypasses the error.","commonSituations":"Bulk close scripts that close an epic without its children; a child was reopened after the epic was assumed done; dependency ordering mistakes where children are closed in the wrong order or partially; imported/migrated data with inconsistent parent-child states.","solutions":["List the epic's children and close all open ones, then retry closing the epic","If the remaining children are genuinely obsolete, close them with an appropriate resolution first","Use the force flag to close the epic anyway when intentionally abandoning open children (e.g. bd close <epic> --force)","Re-parent or remove stale open children that no longer belong to the epic"],"exampleFix":"// before\nbd.Close(epicID) // fails: 3 open children\n// after\nfor _, child := range getChildren(epicID) {\n    if child.Status != types.StatusClosed {\n        bd.Close(child.ID)\n    }\n}\nbd.Close(epicID)","handlingStrategy":"validation","validationCode":"func canCloseEpic(epic *types.Issue, openChildCount int, force bool) bool {\n    return epic != nil && epic.IssueType == types.TypeEpic && (force || openChildCount == 0)\n}","typeGuard":"func epicWithOpenChildren(issue *types.Issue, openChildCount int) bool {\n    return issue != nil && issue.IssueType == types.TypeEpic && openChildCount > 0\n}","tryCatchPattern":"if err := closeIssue(epicID); err != nil {\n    if strings.Contains(err.Error(), \"open child issue\") {\n        return fmt.Errorf(\"close children of %s first or pass --force\", epicID)\n    }\n    return err\n}","preventionTips":["Close children before parents in any scripted teardown/close workflow","Recount open children right before closing an epic (children may have been reopened)","Only use --force when intentionally abandoning open work, and record why","Periodically audit epics for orphaned or stale open children"],"tags":["validation","epic","hierarchy","dependency-order"],"backgroundTag":"epic-has-open-children","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}