fix(file_system): handle PermissionError on scandir in gitignore walker (#1634)

Wrap os.scandir() with PermissionError guard in _iter_gitignore_files'
scan() function to prevent crash when a subdirectory is not readable.

Previously, PermissionError on the scandir call itself crashed the
entire gitignore walker, even when the path was listed in ignored_paths.

Fixes oraios/serena#1624

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
weicongheandClaude authored and GitHub committed 2026-07-02 10:53:22 +02:00
1 parent dcbd8ce064
commit b8a0882d29
3 files changed
+46 -2

No files matched your search

+1
View File
@@ -26,6 +26,7 @@ Status of the `main` branch. Changes prior to the next official version change w
(which does not correctly unpack structured output) #1042
- Fix: Project-specific filtering of files for source files ignored the language backend.
The check is really only possible for LSP.
- Fix: File system permission errors during gitignore scanning were not caught #1624
* CLI:
- Fix `--project-from-cwd` hijacking git worktrees nested under a Serena project. `find_project_root`
+6 -1
View File
@@ -164,7 +164,12 @@ class GitignoreParser:
queue: list[str] = [self.repo_root]
def scan(abs_path: str | None) -> Iterator[str]:
for entry in os.scandir(abs_path):
try:
entries = os.scandir(abs_path)
except PermissionError as ex:
log.debug(f"Skipping unreadable directory {abs_path}: {ex}")
return
for entry in entries:
try:
if entry.is_dir(follow_symlinks=follow_symlinks):
queue.append(entry.path)
+39 -1
View File
@@ -3,7 +3,6 @@ import shutil
import tempfile
from pathlib import Path
# Assuming the gitignore parser code is in a module named 'gitignore_parser'
from pathspec import PathSpec
from serena.util.file_system import GitignoreParser, GitignoreSpec, match_path
@@ -715,3 +714,42 @@ src/*.o
# foo.txt in other/ should NOT be ignored (outside foo/ subtree)
assert not parser.should_ignore("other/foo.txt"), "other/foo.txt should NOT be ignored by foo/.gitignore"
class TestGitignoreParserPermissionError:
"""Test PermissionError handling in GitignoreParser."""
def setup_method(self):
self.test_dir = tempfile.mkdtemp()
self.repo_path = Path(self.test_dir)
def teardown_method(self):
shutil.rmtree(self.test_dir, ignore_errors=True)
def test_scandir_permission_error_on_subdirectory(self):
"""
Test that GitignoreParser does not crash when a subdirectory
is not readable (PermissionError on os.scandir).
Regression test for https://github.com/oraios/serena/issues/1624
"""
# Create a root .gitignore
gitignore = self.repo_path / ".gitignore"
gitignore.write_text("*.log\n")
# Create an unreadable subdirectory
unreadable = self.repo_path / "unreadable_dir"
unreadable.mkdir()
# Remove read permissions (Unix only; on Windows this is a no-op)
old_mode = os.stat(unreadable).st_mode
os.chmod(unreadable, 0o000)
try:
# This should not raise PermissionError
parser = GitignoreParser(str(self.repo_path))
# Parser should still function
assert parser.should_ignore("test.log")
finally:
# Restore permissions so teardown can clean up
os.chmod(unreadable, old_mode)