gastownhall/beads · error

writing migrated hook %s: %w

Error message

writing migrated hook %s: %w

What it means

After the safety guard passes, bd writes the migrated hook with os.WriteFile (mode 0755, since git hooks must be executable). This error wraps any OS-level write failure — permissions, missing directory, disk full, immutable file — with the hook's path for context.

Source

Thrown at cmd/bd/migrate_hooks_apply.go:265

	}

	if err := validateRetireCollisionPolicy(execPlan.RetireOps); err != nil {
		return hookMigrationApplySummary{}, err
	}

	summary := hookMigrationApplySummary{
		WrittenHooks:     make([]string, 0, len(preparedWrites)),
		RetiredArtifacts: make([]string, 0, len(execPlan.RetireOps)),
		SkippedArtifacts: make([]string, 0),
	}

	for _, write := range preparedWrites {
		if err := guardHookWritePath(write.Path, false); err != nil {
			return summary, fmt.Errorf("refusing to write migrated hook %s: %w", write.HookName, err)
		}
		// #nosec G306 -- git hooks must be executable for Git to run them
		if err := os.WriteFile(write.Path, write.Content, 0755); err != nil {
			return summary, fmt.Errorf("writing migrated hook %s: %w", write.Path, err)
		}
		summary.WrittenHooks = append(summary.WrittenHooks, write.HookName)
	}

	for _, retire := range execPlan.RetireOps {
		retired, retiredErr := retireHookSidecar(retire)
		if retiredErr != nil {
			return summary, retiredErr
		}
		if retired == "" {
			summary.SkippedArtifacts = append(summary.SkippedArtifacts, retire.SourcePath)
			continue
		}
		summary.RetiredArtifacts = append(summary.RetiredArtifacts, retired)
	}

	summary.WrittenHookCount = len(summary.WrittenHooks)
	summary.RetiredCount = len(summary.RetiredArtifacts)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix permissions: `chown` the hooks directory to the current user and ensure it's writable
  2. Check the mount is not read-only (`mount | grep <path>`)
  3. Free disk space if the filesystem is full
  4. Retry `bd migrate hooks apply` once the filesystem condition is fixed
Defensive patterns

Strategy: try-catch

Validate before calling

// verify writability before applying
fs.accessSync(hooksDir, fs.constants.W_OK);

Try / catch

if err := apply(...); err != nil && strings.Contains(err.Error(), "writing migrated hook") {
    // inspect wrapped OS error: check permissions, disk space, read-only mount, then retry
}

Prevention

When it happens

Trigger: os.WriteFile(write.Path, write.Content, 0755) returned an error during applyHookMigrationExecution, after guardHookWritePath succeeded.

Common situations: Read-only checkout or hooks directory owned by root/another user; worktree on a read-only mount; disk full; path disappeared between guard and write.

Related errors


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