gastownhall/beads · error

created worktree is dirty after checkout; refusing to contin

Error message

created worktree is dirty after checkout; refusing to continue: %s
%s

What it means

After creating a worktree, bd runs `git status --porcelain` and refuses to continue if the output is non-empty — the freshly checked-out worktree must be clean. This is an integrity check: a dirty new worktree means something (a hook, filter, or stale files) modified or added files during/right after checkout.

Source

Thrown at cmd/bd/worktree_cmd.go:455

		fmt.Printf("  Beads: local (no redirect)\n")
	}

	return nil
}

// Helper functions

var checkCreatedWorktreeClean = ensureCreatedWorktreeClean

func ensureCreatedWorktreeClean(ctx context.Context, worktreePath string) error {
	gitCmd := gitCmdInDir(ctx, worktreePath, "status", "--porcelain=v1", "--untracked-files=all")
	output, err := gitCmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("failed to inspect created worktree cleanliness: %w\n%s", err, string(output))
	}

	if status := strings.TrimSpace(string(output)); status != "" {
		return fmt.Errorf("created worktree is dirty after checkout; refusing to continue: %s\n%s", worktreePath, status)
	}

	return nil
}

// gitCmdInDir creates a git command that runs in the specified directory.
// This is used for worktree operations that need to run in a specific location
// (either the CWD repo root or a specific worktree path).
//
// Security: Sets core.hooksPath and GIT_TEMPLATE_DIR to disable hooks/templates
// for defense-in-depth, matching the pattern in RepoContext.GitCmd().
func gitCmdInDir(ctx context.Context, dir string, args ...string) *exec.Cmd {
	gitArgs := append([]string{"-c", "core.hooksPath="}, args...)
	cmd := exec.CommandContext(ctx, "git", gitArgs...)
	cmd.Dir = dir
	// Security: Disable git hooks and templates (SEC-001, SEC-002)
	cmd.Env = append(os.Environ(),
		"GIT_TEMPLATE_DIR=",

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the status list in the error message to see which files are dirty
  2. Remove the worktree and retry with a clean path: `git worktree remove <path> --force`
  3. Add appropriate .gitignore entries so generated/untracked files don't appear
  4. Check for smudge filters or hooks modifying files at checkout (git config filter.*, core.hooksPath)

Example fix

// before
bd worktree create ../wt-feature -b feature   # fails: untracked build.log
// after
echo build.log >> .gitignore
git worktree remove ../wt-feature --force
bd worktree create ../wt-feature -b feature
Defensive patterns

Strategy: validation

Validate before calling

status=$(git -C "$WT_PATH" status --porcelain=v1 --untracked-files=all)
if [ -n "$status" ]; then echo "worktree dirty: $status"; exit 1; fi

Try / catch

if status := strings.TrimSpace(string(output)); status != "" {
    // clean the worktree or fix the filter/hook that dirtied it before retrying
    return fmt.Errorf("worktree %s dirty: %s", wtPath, status)
}

Prevention

When it happens

Trigger: `bd worktree create` produces a worktree whose `git status --porcelain=v1 --untracked-files=all` lists modified or untracked files immediately after checkout — e.g. smudge filters writing files, pre-existing files colliding with checked-out paths, or generated files appearing from build hooks.

Common situations: A target branch's checkout triggers git-lfs smudging that fails partway; .gitignore missing so build artifacts inside the worktree path show as untracked; a previous worktree at the same path left files behind that git checkout did not overwrite.

Related errors


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