gastownhall/beads · error

hook migration is blocked: - %s

Error message

hook migration is blocked:
- %s

What it means

During a pending 'hooks' migration, fixPendingMigrations builds an execution plan and checks execPlan.BlockingErrors. If any blocking errors exist, it refuses to proceed and returns this error listing them. This is a deliberate safety stop, not a crash: applying the migration would break or overwrite hooks.

Source

Thrown at cmd/bd/doctor_fix.go:453

}

func fixPendingMigrations(path string) error {
	pending := doctor.DetectPendingMigrations(path)
	if len(pending) == 0 {
		return nil
	}

	for _, migration := range pending {
		switch migration.Name {
		case "hooks":
			plan, err := doctor.PlanHookMigration(path)
			if err != nil {
				return fmt.Errorf("building hook migration plan: %w", err)
			}

			execPlan := buildHookMigrationExecutionPlan(plan)
			if len(execPlan.BlockingErrors) > 0 {
				return fmt.Errorf("hook migration is blocked:\n- %s", strings.Join(execPlan.BlockingErrors, "\n- "))
			}

			summary, err := applyHookMigrationExecution(execPlan)
			if err != nil {
				return fmt.Errorf("applying hook migration: %w", err)
			}

			fmt.Printf(
				"  Hook migration applied: %d hook(s) written, %d artifact(s) retired, %d artifact(s) skipped\n",
				summary.WrittenHookCount,
				summary.RetiredCount,
				summary.SkippedCount,
			)
		default:
			return fmt.Errorf("no automatic fix available for pending migration %q", migration.Name)
		}
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the bullet list of blocking errors in the message and resolve each one (usually preserve or move your custom hooks)
  2. Back up conflicting hook files and re-run `bd fix`
  3. If another hook manager owns the hooks, configure that manager to invoke the beads hook instead of letting bd overwrite
  4. Re-run `bd doctor` after fixing to confirm the migration can proceed

Example fix

// before: custom hook clobbered by migration
#!/bin/sh
exec my-linter
// after: chain the beads hook
#!/bin/sh
bd hook "$@" || exit 1
exec my-linter
Defensive patterns

Strategy: validation

Validate before calling

plan, err := doctor.PlanHookMigration(path)
if err == nil {
  execPlan := buildHookMigrationExecutionPlan(plan)
  for _, e := range execPlan.BlockingErrors { fmt.Println("blocking:", e) }
}

Try / catch

if err := fixPendingMigrations(pending, path); err != nil {
  if strings.HasPrefix(err.Error(), "hook migration is blocked") {
    fmt.Fprintln(os.Stderr, "Resolve blocking errors manually, then re-run.")
    return err // do not retry blindly
  }
}

Prevention

When it happens

Trigger: `bd fix` / `bd doctor --fix` with a pending hooks migration where buildHookMigrationExecutionPlan reports one or more BlockingErrors — e.g. a user-modified hook file that would be overwritten, unwritable hook path, or conflicting hook content.

Common situations: Developer customized pre-commit/post-commit hooks manually; hooks managed by another tool (husky, pre-commit framework) conflict with beads' hooks.

Related errors


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