Bug
The getProtoImportPaths function in gazelle/buf/generate.go uses fs.WalkDir to discover .proto files under the module root, but does not skip hidden directories (names starting with .). This causes it to discover proto files in directories like .git/, .claude/, or other tool-managed hidden directories, generating buf_breaking_test targets that reference nonexistent packages and break bazel build //....
Gazelle's own directory walker skips hidden directories by default, so the buf extension's behavior is inconsistent with the rest of Gazelle.
Repro
- Have a
buf.yaml v2 config with modules: [{path: "."}] and # gazelle:buf_breaking_against set to module mode
- Have any hidden directory under the workspace root that contains
.proto files (e.g. .claude/worktrees/ from Claude Code, or any git worktree under a dotdir)
- Run
bazel run //:gazelle
The generated buf_breaking_test in the root BUILD.bazel will include targets like:
buf_breaking_test(
name = "buf_breaking",
targets = [
# ... legitimate targets ...
"@buf_deps//.claude/worktrees/some-worktree/apps/my-app/proto:proto_proto",
# ... hundreds more from each worktree ...
],
)
These @buf_deps// targets don't exist (the @buf_deps workspace mirror correctly skips dotdirs), so bazel build //... fails.
Root cause
In gazelle/buf/generate.go:
func getProtoImportPaths(config *Config, moduleRoot string) []string {
var targets []string
fs.WalkDir(
os.DirFS(moduleRoot),
".",
func(path string, dirEntry fs.DirEntry, err error) error {
if dirEntry.IsDir() {
return nil // enters ALL directories, including hidden ones
}
// ...
},
)
return targets
}
When dirEntry.IsDir() is true, the function returns nil (continue walking) instead of returning fs.SkipDir for hidden directories.
Additionally, the function does not respect the includes field from buf.yaml v2 module config — it walks the entire module root regardless. The excludes field cannot be used as a workaround because buf v2 validates that exclude paths must be within include paths.
Suggested fix
func(path string, dirEntry fs.DirEntry, err error) error {
if dirEntry.IsDir() {
// Skip hidden directories (e.g. .git, .claude) to match
// Gazelle's own directory-walking behavior.
if path != "." && strings.HasPrefix(dirEntry.Name(), ".") {
return fs.SkipDir
}
return nil
}
A more complete fix would also filter by includes when present in the v2 module config, but skipping hidden directories addresses the most common case and matches Gazelle's conventions.
Workaround
We're currently applying this as a patch via archive_override in MODULE.bazel:
archive_override(
module_name = "rules_buf",
patch_strip = 1,
patches = ["//third-party/patches:rules_buf_skip_hidden_dirs.patch"],
# ...
)
Environment
- rules_buf 0.5.2
- Bazel 7.x with bzlmod
buf.yaml v2 with module mode breaking checks
Bug
The
getProtoImportPathsfunction ingazelle/buf/generate.gousesfs.WalkDirto discover.protofiles under the module root, but does not skip hidden directories (names starting with.). This causes it to discover proto files in directories like.git/,.claude/, or other tool-managed hidden directories, generatingbuf_breaking_testtargets that reference nonexistent packages and breakbazel build //....Gazelle's own directory walker skips hidden directories by default, so the buf extension's behavior is inconsistent with the rest of Gazelle.
Repro
buf.yamlv2 config withmodules: [{path: "."}]and# gazelle:buf_breaking_againstset to module mode.protofiles (e.g..claude/worktrees/from Claude Code, or any git worktree under a dotdir)bazel run //:gazelleThe generated
buf_breaking_testin the rootBUILD.bazelwill include targets like:These
@buf_deps//targets don't exist (the@buf_depsworkspace mirror correctly skips dotdirs), sobazel build //...fails.Root cause
In
gazelle/buf/generate.go:When
dirEntry.IsDir()is true, the function returnsnil(continue walking) instead of returningfs.SkipDirfor hidden directories.Additionally, the function does not respect the
includesfield frombuf.yamlv2 module config — it walks the entire module root regardless. Theexcludesfield cannot be used as a workaround because buf v2 validates that exclude paths must be within include paths.Suggested fix
A more complete fix would also filter by
includeswhen present in the v2 module config, but skipping hidden directories addresses the most common case and matches Gazelle's conventions.Workaround
We're currently applying this as a patch via
archive_overrideinMODULE.bazel:Environment
buf.yamlv2 with module mode breaking checks