From a0d24f37af2fe15c1d173b01b807794d2c1f34f3 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Thu, 6 Nov 2025 19:07:46 -0800 Subject: [PATCH] Fix bd-1ezg: Prevent import/export from hanging when daemon is running Root cause: Import and export commands tried to open the database directly while the daemon already held the lock, causing indefinite blocking. Solution: Both commands now explicitly close the daemon connection before opening direct database access, avoiding SQLite lock contention. Changes: - import.go: Close daemon connection and open direct SQLite connection - export.go: Close daemon connection before direct access - Added debug logging to help diagnose similar issues Tests: Existing TestImport and TestExport tests pass --- cmd/bd/export.go | 12 ++++++++++-- cmd/bd/import.go | 25 ++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cmd/bd/export.go b/cmd/bd/export.go index e2cd7455..6e2fc86a 100644 --- a/cmd/bd/export.go +++ b/cmd/bd/export.go @@ -95,10 +95,18 @@ Output to stdout by default, or use -o flag for file output.`, os.Exit(1) } - // Export command doesn't work with daemon - need direct access + // Export command requires direct database access for consistent snapshot + // If daemon is connected, close it and open direct connection + if daemonClient != nil { + if os.Getenv("BD_DEBUG") != "" { + fmt.Fprintf(os.Stderr, "Debug: export command forcing direct mode (closes daemon connection)\n") + } + _ = daemonClient.Close() + daemonClient = nil + } + // Ensure we have a direct store connection if store == nil { - // Initialize store directly even if daemon is running var err error if dbPath == "" { fmt.Fprintf(os.Stderr, "Error: no database path found\n") diff --git a/cmd/bd/import.go b/cmd/bd/import.go index b22bc24c..2bb490e7 100644 --- a/cmd/bd/import.go +++ b/cmd/bd/import.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/steveyegge/beads/internal/storage/sqlite" "github.com/steveyegge/beads/internal/types" ) @@ -25,8 +26,30 @@ Behavior: - New issues are created - Collisions (same ID, different content) are detected and reported - Use --dedupe-after to find and merge content duplicates after import - - Use --dry-run to preview changes without applying them`, + - Use --dry-run to preview changes without applying them + +NOTE: Import requires direct database access and does not work with daemon mode. + The command automatically uses --no-daemon when executed.`, Run: func(cmd *cobra.Command, args []string) { + // Import requires direct database access due to complex transaction handling + // and collision detection. Force direct mode regardless of daemon state. + if daemonClient != nil { + if os.Getenv("BD_DEBUG") != "" { + fmt.Fprintf(os.Stderr, "Debug: import command forcing direct mode (closes daemon connection)\n") + } + _ = daemonClient.Close() + daemonClient = nil + + // Now initialize direct store + var err error + store, err = sqlite.New(dbPath) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err) + os.Exit(1) + } + defer func() { _ = store.Close() }() + } + input, _ := cmd.Flags().GetString("input") skipUpdate, _ := cmd.Flags().GetBool("skip-existing") strict, _ := cmd.Flags().GetBool("strict")