rust-lang/rust-analyzer · error

No files to search

Error message

No files to search

What it means

ide-ssr's `at_first_file` entry point requires at least one file to operate on: it picks the first file, creates a MatchFinder rooted at that file position, and only then applies SSR rules. When the supplied file list (or the resolution of the requested file) yields no file id, the library cannot anchor the search anywhere, so it fails fast with `bail!("No files to search")`.

Source

Thrown at crates/ide-ssr/src/lib.rs:156

        .ok_or_else(|| SsrError("no resolution scope for file".into()))?;
        Ok(MatchFinder { sema, rules: Vec::new(), resolution_scope, restrict_ranges })
    }

    /// Constructs an instance using the start of the first file in `db` as the lookup context.
    pub fn at_first_file(db: &'db ide_db::RootDatabase) -> Result<MatchFinder<'db>, SsrError> {
        if let Some(first_file_id) = LocalRoots::get(db)
            .roots(db)
            .iter()
            .next()
            .and_then(|root| db.source_root(*root).source_root(db).iter().next())
        {
            MatchFinder::in_context(
                db,
                ide_db::FilePosition { file_id: first_file_id, offset: 0.into() },
                vec![],
            )
        } else {
            bail!("No files to search");
        }
    }

    /// Adds a rule to be applied. The order in which rules are added matters. Earlier rules take
    /// precedence. If a node is matched by an earlier rule, then later rules won't be permitted to
    /// match to it.
    pub fn add_rule(&mut self, rule: SsrRule) -> Result<(), SsrError> {
        for parsed_rule in rule.parsed_rules {
            self.rules.push(ResolvedRule::new(
                parsed_rule,
                &self.resolution_scope,
                self.rules.len(),
            )?);
        }
        Ok(())
    }

    /// Finds matches for all added rules and returns edits for all found matches.

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Ensure at least one file is resolved to a valid `FileId` before calling the SSR entry point.
  2. Check the file collection for emptiness and bail early in your own code with a clearer message (e.g. 'open a file first').
  3. If files should exist, verify the VFS/load path: the file may not be added to the database yet.
  4. Fall back to the multi-file API (`MatchFinder::in_context` with your own file list) only after confirming it is non-empty.

Example fix

// before
let ssr = SsrPattern::at_first_file(db, /* files possibly empty */)?;
// after
anyhow::ensure!(!file_ids.is_empty(), "SSR requires at least one open file");
let ssr = SsrPattern::at_first_file(db, file_ids)?;
Defensive patterns

Strategy: validation

Validate before calling

// caller-side pre-check before invoking SSR
if file_ids.is_empty() {
    return Err(anyhow::anyhow!("SSR needs at least one resolved file; got none"));
}
let ssr = SsrPattern::at_first_file(db, file_ids)?;

Type guard

fn has_files(files: &[FileId]) -> bool { !files.is_empty() }

Try / catch

match SsrPattern::at_first_file(db, file_ids) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("No files to search") => {
        eprintln!("No files loaded for SSR; open/resolve a file first");
        return Ok(());
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling `SsrPattern/MatchFinder::at_first_file` (or the ide-ssr API backed by it, e.g. structural search/replace via `ide_db` handlers) with an empty file-id vector, or with input whose file resolution produced no valid `FileId` (e.g. an unopened/nonexistent file filtered out upstream).

Common situations: Batch refactoring scripts driven over a VFS snapshot where no files were loaded yet; editors invoking SSR before the workspace finished loading; callers passing a path that failed to resolve to a FileId so the resulting collection is empty.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/5e6b63966a201ae2. Report an issue: GitHub.