{"record":{"id":"c968b2a95da1fc38","repo":"gastownhall/beads","slug":"failed-to-remove-dolt-database-w","errorCode":null,"errorMessage":"failed to remove Dolt database: %w","messagePattern":"failed to remove Dolt database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/fix/repo_fingerprint.go","lineNumber":160,"sourceCode":"\t\tisDolt := cfg.GetBackend() == configfile.BackendDolt\n\n\t\t// Confirm before destructive action\n\t\tfmt.Printf(\"  ⚠️  This will DELETE %s. Continue? [y/N]: \", dbPath)\n\t\tconfirm, err := repoFingerprintReadLine()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to read confirmation: %w\", err)\n\t\t}\n\t\tconfirm = strings.TrimSpace(strings.ToLower(confirm))\n\t\tif confirm != \"y\" && confirm != \"yes\" {\n\t\t\tfmt.Println(\"  → Skipped (canceled)\")\n\t\t\treturn nil\n\t\t}\n\n\t\t// Remove database and reinitialize in-process\n\t\tfmt.Printf(\"  → Removing %s...\\n\", dbPath)\n\t\tif isDolt {\n\t\t\tif err := os.RemoveAll(dbPath); err != nil && !os.IsNotExist(err) {\n\t\t\t\treturn fmt.Errorf(\"failed to remove Dolt database: %w\", err)\n\t\t\t}\n\t\t} else {\n\t\t\tif err := os.Remove(dbPath); err != nil && !os.IsNotExist(err) {\n\t\t\t\treturn fmt.Errorf(\"failed to remove database: %w\", err)\n\t\t\t}\n\t\t\t_ = os.Remove(dbPath + \"-wal\")\n\t\t\t_ = os.Remove(dbPath + \"-shm\")\n\t\t}\n\n\t\t// Reinitialize by creating a new store (auto-bootstraps from JSONL)\n\t\tfmt.Println(\"  → Reinitializing database from JSONL...\")\n\t\tctx := context.Background()\n\t\tstore, err := dolt.NewFromConfig(ctx, beadsDir)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to initialize database: %w\", err)\n\t\t}\n\t\tdefer func() { _ = store.Close() }()\n","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/fix/repo_fingerprint.go#L142-L178","documentation":"After the user confirms option [2], the Dolt backend path removes the database directory with `os.RemoveAll(dbPath)`. If the removal fails for a reason other than 'does not exist', the error is wrapped as `failed to remove Dolt database: %w`. This usually means something is holding the database open or the process lacks filesystem permissions — the reinitialize step is then not attempted.","triggerScenarios":"`os.RemoveAll(dbPath)` returns a non-IsNotExist error: the `.beads` Dolt database directory is locked/open by another `bd` process or Dolt server, the directory or a file inside is read-only, or the user lacks write permission on the parent directory.","commonSituations":"A `bd daemon` or another bd session still has the Dolt database open; running the fix without sufficient permissions (e.g. database owned by another user); filesystem mounted read-only; NFS/network mounts with stale locks.","solutions":["Stop all bd processes holding the database (`bd daemon --stop`, close other sessions/terminals) and retry.","Check and fix permissions on the `.beads` directory and its parent (`ls -la .beads`, `chown`/`chmod` as needed).","Inspect the wrapped error via `errors.Unwrap` for the exact OS reason (EBUSY, EACCES, EROFS) and address it.","Remove the directory manually once nothing holds it, then re-run the fix or let bd re-bootstrap from `.beads/issues.jsonl`."],"exampleFix":"// before: fix fails while daemon holds the database\nbd doctor --fix            // fails: failed to remove Dolt database: device or resource busy\n// after\nbd daemon --stop\nbd doctor --fix            # removal and reinit succeed","handlingStrategy":"try-catch","validationCode":"// Ensure every prompt can be answered before choosing option 2\nanswers := \"2\\ny\\n\"\ncmd := exec.Command(\"bd\", \"doctor\", \"--fix\")\ncmd.Stdin = strings.NewReader(answers) // two lines: choice + confirmation\ncmd.Stdout = os.Stdout","typeGuard":"if errors.Is(err, io.EOF) {\n\t// confirmation input never arrived; treat as canceled, database untouched\n}","tryCatchPattern":"if err := fix.RepoFingerprint(path, false); err != nil {\n\tvar wrapped interface{ Unwrap() error }\n\tif errors.As(err, &wrapped) && errors.Is(wrapped, io.EOF) {\n\t\tfmt.Println(\"confirmation not received; destructive action safely skipped\")\n\t} else if err != nil {\n\t\tlog.Fatal(err)\n\t}\n}","preventionTips":["When automating, pipe one line per prompt: `printf '2\\ny\\n' | bd ...`.","Use --yes for unattended runs; it never reaches the confirmation prompt and never deletes.","Remember any missing stdin line surfaces as an error; never assume the first answer carries over.","Wrap stdin providers (expect/script) around interactive runs if a TTY is required."],"tags":["go","filesystem","dolt","os-removeall","permissions"],"backgroundTag":"file-delete-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}