gastownhall/beads · error

failed to migrate database %s to %s: %w

Error message

failed to migrate database %s to %s: %w

What it means

migrateOldDatabases renames the single discovered legacy database (os.Rename(oldDB, targetPath)) to the canonical beads.db name. This error wraps that rename failure, naming both the source and destination paths. bd throws it because the migration must be atomic: if the rename fails the old database stays put and init aborts rather than creating a fresh, empty database beside existing data.

Source

Thrown at cmd/bd/init.go:2408

		// No old databases to migrate
		return nil
	}

	if len(oldDBs) > 1 {
		// Multiple databases found - ambiguous, require manual intervention
		return fmt.Errorf("multiple database files found in %s: %v\nPlease manually rename the correct database to %s and remove others",
			targetDir, oldDBs, targetName)
	}

	// Migrate the single old database
	oldDB := oldDBs[0]
	if !quiet {
		fmt.Fprintf(os.Stderr, "→ Migrating database: %s → %s\n", filepath.Base(oldDB), targetName)
	}

	// Rename the old database to the new canonical name
	if err := os.Rename(oldDB, targetPath); err != nil {
		return fmt.Errorf("failed to migrate database %s to %s: %w", oldDB, targetPath, err)
	}

	if !quiet {
		fmt.Fprintf(os.Stderr, "✓ Database migration complete\n\n")
	}

	return nil
}

// errWorkspaceAlreadyInitialized marks the benign "this workspace already has a
// database" outcome from checkExistingBeadsData. --init-if-missing treats only
// this case as an idempotent skip; any other error from the check is operational
// (e.g. an unreadable .beads/embeddeddolt directory) and must still abort rather
// than be silently masked as success.
var errWorkspaceAlreadyInitialized = errors.New("workspace already initialized")

// workspaceExistsError carries a user-facing "already initialized" message while
// still matching errWorkspaceAlreadyInitialized via errors.Is, so callers can

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check for and remove any partially created target file at the destination path, then re-run bd init.
  2. If source and target are on different filesystems, move the file manually into .beads first (cp then rm) and re-run init.
  3. Fix ownership/permissions on the old .db file and the .beads directory (e.g. sudo chown -R $USER .beads) if the process cannot rename it.
  4. On network/overlay filesystems where rename fails, copy the database manually: mv <old>.db .beads/beads.db, then run bd init again.

Example fix

// before
$ bd init
# failed to migrate database .beads/issues.db to .beads/beads.db: rename ... invalid cross-device link

// after: move within the same filesystem
$ mv -f .beads/issues.db .beads/beads.db
$ bd init  # target exists, migration skipped
Defensive patterns

Strategy: retry

Validate before calling

// Preflight: ensure rename is possible on the same filesystem
if fi, err := os.Stat(targetPath); err == nil {
    return fmt.Errorf("target %s already exists (size %d); remove it or skip migration", targetPath, fi.Size())
}
if sameDevice, _ := onSameFilesystem(oldDB, targetPath); !sameDevice {
    return fmt.Errorf("%s and %s are on different filesystems; copy manually", oldDB, targetPath)
}

Try / catch

if err := migrateOldDatabases(targetPath, quiet); err != nil {
    var pe *fs.PathError
    if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EXDEV) {
        // fall back to copy+delete across filesystems, then re-run init
    }
    return err
}

Prevention

When it happens

Trigger: Running `bd init` when os.Rename(oldDB, targetPath) fails — most commonly the target path already exists (though the function stats it earlier, a race or symlink can cause EXDEV/EPERM/EEXIST), source and destination are on different filesystems (EXDEV), or the source file vanished or is not writable by the current user.

Common situations: Running on a filesystem that does not support atomic rename semantics (some network mounts return EXDEV or ENOSYS); another bd process or editor recreating beads.db concurrently; the legacy .db file sitting on a different volume than .beads (e.g. BEADS_DIR on a symlink into another mount); permission changes after the file was created by another user (e.g. root in a container).

Related errors


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