gastownhall/beads · error

failed to clear ephemeral flag on root %s: %w

Error message

failed to clear ephemeral flag on root %s: %w

What it means

After closing a wisp root, squash clears its ephemeral flag (sets wisp=false) so the closed root stops being re-emitted by wisp-table export cycles. If w.UpdateIssue fails, the error is wrapped as 'failed to clear ephemeral flag on root %s: %w' and the transaction rolls back.

Source

Thrown at cmd/bd/mol_squash.go:330

	// Delete ephemeral children within the same transaction
	if !keepChildren {
		for _, id := range childIDs {
			if err := w.DeleteIssue(ctx, id, actorName); err != nil {
				return nil, fmt.Errorf("failed to delete child %s: %w", id, err)
			}
			result.DeletedCount++
		}
	}

	// Auto-close the root if it's a wisp — squash completes the molecule lifecycle
	if root.Ephemeral {
		reason := fmt.Sprintf("Squashed: %d steps → digest %s", len(children), result.DigestID)
		if err := w.CloseIssue(ctx, root.ID, reason, actorName); err != nil {
			return nil, fmt.Errorf("failed to close wisp root %s: %w", root.ID, err)
		}
		// Clear ephemeral so the closed root stops being re-emitted by every wisp-table export cycle.
		if err := w.UpdateIssue(ctx, root.ID, map[string]interface{}{"wisp": false}, actorName); err != nil {
			return nil, fmt.Errorf("failed to clear ephemeral flag on root %s: %w", root.ID, err)
		}
		result.WispSquash = true
	}

	return result, nil
}

func init() {
	molSquashCmd.Flags().Bool("dry-run", false, "Preview what would be squashed")
	molSquashCmd.Flags().Bool("keep-children", false, "Don't delete ephemeral children after squash")
	molSquashCmd.Flags().String("summary", "", "Agent-provided summary (bypasses auto-generation)")

	molCmd.AddCommand(molSquashCmd)
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause; run any pending migrations and bd doctor to verify the wisp field exists
  2. Stop competing bd processes/syncs, then retry the squash
  3. If schema mismatch persists, back up and re-migrate the database before squashing
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure schema supports the wisp field before squashing ephemeral roots
if root.Ephemeral && !schemaHasWispField() {
    return fmt.Errorf("database schema outdated; run migrations")
}

Type guard

func isWisp(i *types.Issue) bool { return i != nil && i.Ephemeral }

Try / catch

err := squashMolecule(ctx, s, root, children, keep, summary, actor)
if err != nil {
    if strings.Contains(err.Error(), "failed to clear ephemeral flag") {
        log.Printf("flag clear failed; rerun migrations: %v", errors.Unwrap(err))
    }
    return err
}

Prevention

When it happens

Trigger: Squashing an ephemeral root when the flag-clearing update fails: storage write error, database lock, schema mismatch on the wisp/ephemeral column, or root deleted concurrently.

Common situations: Older database schema lacking the wisp field; export/sync process holding locks; interrupted upgrade leaving partial wisp metadata.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/c61b5885fd199c14. Report an issue: GitHub.