astral-sh/ruff · error · anyhow::Error
Expected at least one path to search for Python files
Error message
Expected at least one path to search for Python files
What it means
Guard inside `project_files_in_path` (crates/ruff_workspace/src/resolver.rs:429), the shared entry point ruff and ty use to walk paths for Python files. After normalizing and deduplicating the input slice, the function needs a first path to seed `ignore::WalkBuilder`; an empty slice violates that invariant and is converted into this anyhow error. Note that the `force-exclude` branch just above returns `Ok((vec![], resolver))` early when exclusions remove everything, so only a caller that passes zero paths up front can actually reach it.
Source
Thrown at crates/ruff_workspace/src/resolver.rs:478
} else {
// We already visited this ancestor, we can stop here.
break;
}
}
}
}
// Check if the paths themselves are excluded.
if resolver.force_exclude() {
paths.retain(|path| !is_file_excluded(path, &resolver));
if paths.is_empty() {
return Ok((vec![], resolver));
}
}
let (first_path, rest_paths) = paths
.split_first()
.ok_or_else(|| anyhow!("Expected at least one path to search for Python files"))?;
// Create the `WalkBuilder`.
let mut builder = WalkBuilder::new(first_path);
if let Ok(cwd) = std::env::current_dir() {
builder.current_dir(cwd);
}
for path in rest_paths {
builder.add(path);
}
builder.standard_filters(resolver.respect_gitignore());
builder.hidden(false);
builder.threads(
std::thread::available_parallelism()
.map_or(1, std::num::NonZeroUsize::get)
.min(12),
);View on GitHub (pinned to 672bb4edf0)
Solutions
- Default the path list to the current directory when your collection step yields nothing: `if paths.is_empty() { paths.push(std::env::current_dir()?); }`
- Log `paths.len()` immediately before the call to find which upstream filter emptied it and fix that filter
- If you only need CLI behavior, shell out to `ruff check .` / `ty check .` instead of calling the function directly
Example fix
// before
let files = project_files_in_path(&paths, &pyproject_config, &transformer)?; // paths == vec![]
// after
let paths = if paths.is_empty() {
vec![std::env::current_dir()?]
} else {
paths
};
let files = project_files_in_path(&paths, &pyproject_config, &transformer)?; Defensive patterns
Strategy: validation
Validate before calling
if paths.is_empty() {
anyhow::bail!("no paths to search: refusing to call project_files_in_path");
} Try / catch
let result = project_files_in_path(&paths, &cfg, &transformer);
if let Err(e) = &result {
if e.to_string().contains("Expected at least one path") {
// caller bug: fix the empty input; do not retry
}
} Prevention
- Never pass an empty path slice; default to the current directory when your filter empties the set
- Compute and validate the file set before calling into ruff_workspace
- Treat this message as an assertion failure in your glue code, not an environmental error to retry
When it happens
Trigger: Calling `ruff_workspace::resolver::project_files_in_path(&[], &pyproject_config, &transformer)` with an empty path slice — typically because an upstream glob or filter removed every entry before the call. The bundled CLIs always default to `.`, so end users of `ruff`/`ty` effectively never see this.
Common situations: Editor integrations or custom runners built on ruff_workspace that compute the file set themselves (empty workspace, empty glob result, or all candidate paths pre-filtered out) and forward the empty Vec unchanged.
Related errors
AI-assisted analysis of astral-sh/ruff@672bb4edf0 (2026-08-16).
Data as JSON: /api/errors/f719c6ee4b2b0a03.
Report an issue: GitHub.