{"record":{"id":"31e3d002c25f9ca6","repo":"gastownhall/beads","slug":"failed-to-close-wisp-root-s-w","errorCode":null,"errorMessage":"failed to close wisp root %s: %w","messagePattern":"failed to close wisp root (.+?): %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/mol_squash.go","lineNumber":326,"sourceCode":"\tif err := w.AddDependency(ctx, dep, actorName); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to link digest to root: %w\", err)\n\t}\n\n\t// Delete ephemeral children within the same transaction\n\tif !keepChildren {\n\t\tfor _, id := range childIDs {\n\t\t\tif err := w.DeleteIssue(ctx, id, actorName); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to delete child %s: %w\", id, err)\n\t\t\t}\n\t\t\tresult.DeletedCount++\n\t\t}\n\t}\n\n\t// Auto-close the root if it's a wisp — squash completes the molecule lifecycle\n\tif root.Ephemeral {\n\t\treason := fmt.Sprintf(\"Squashed: %d steps → digest %s\", len(children), result.DigestID)\n\t\tif err := w.CloseIssue(ctx, root.ID, reason, actorName); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to close wisp root %s: %w\", root.ID, err)\n\t\t}\n\t\t// Clear ephemeral so the closed root stops being re-emitted by every wisp-table export cycle.\n\t\tif err := w.UpdateIssue(ctx, root.ID, map[string]interface{}{\"wisp\": false}, actorName); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to clear ephemeral flag on root %s: %w\", root.ID, err)\n\t\t}\n\t\tresult.WispSquash = true\n\t}\n\n\treturn result, nil\n}\n\nfunc init() {\n\tmolSquashCmd.Flags().Bool(\"dry-run\", false, \"Preview what would be squashed\")\n\tmolSquashCmd.Flags().Bool(\"keep-children\", false, \"Don't delete ephemeral children after squash\")\n\tmolSquashCmd.Flags().String(\"summary\", \"\", \"Agent-provided summary (bypasses auto-generation)\")\n\n\tmolCmd.AddCommand(molSquashCmd)\n}","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/mol_squash.go#L308-L344","documentation":"When the squashed molecule root is ephemeral (a wisp), squash auto-closes it to complete the molecule lifecycle. If w.CloseIssue fails, the error is wrapped as 'failed to close wisp root %s: %w', aborting the transaction and rolling back the squash.","triggerScenarios":"Squashing a wisp molecule when closing the root fails: root deleted concurrently, status transition rejected by validation, or storage write error/lock.","commonSituations":"Concurrent process already closed or deleted the root; custom status workflows rejecting the close transition; DB lock contention during sync.","solutions":["Check the wrapped cause; confirm the root still exists and is not already closed","Resolve concurrent writers (another bd session or active sync) and retry","If status validation rejects the close, adjust the root's status/workflow before squashing","Retry the squash after storage is healthy; the digest creation will redo atomically"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Check root is open and still exists before squash\nroot, err := s.GetIssue(ctx, rootID)\nif err != nil || root.Status == \"closed\" {\n    return fmt.Errorf(\"root %s missing or already closed\", rootID)\n}","typeGuard":"func canClose(i *types.Issue) bool { return i != nil && i.Status != \"closed\" }","tryCatchPattern":"err := squashMolecule(ctx, s, root, children, keep, summary, actor)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to close wisp root\") {\n        log.Printf(\"wisp close failed; transaction rolled back: %v\", errors.Unwrap(err))\n    }\n    return err\n}","preventionTips":["Verify the root is not already closed/deleted before squashing","Pause sync operations during molecule mutations","Handle status-transition validation rules in custom workflows"],"tags":["go","storage","close","wisp","transaction"],"backgroundTag":"issue-close-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}