[rust] Selenium Manager panics instead of degrading when the metadata cache cannot be read
Description
Selenium Manager treats an unusable se-metadata.json cache inconsistently: some failures degrade gracefully, one aborts the process. This came up reviewing #18087, which improves the diagnostic for the fatal arm but deliberately leaves the behaviour as-is.
se-metadata.json is a pure cache — TTL'd browser/driver version lookups plus last_used stamps for cache pruning. Losing it costs one round of re-discovery, nothing more. Today in rust/src/metadata.rs:
| Failure | Current behaviour |
|---|---|
| File absent | new_metadata(log) — empty cache |
Unparseable JSON (metadata.rs:110) |
Err(_e) => new_metadata(log) — empty cache, silently |
Write fails (metadata.rs:243) |
log.warn("Metadata cannot be written in cache ...") |
Delete fails (metadata.rs:262) |
log.warn("Error deleting metadata file: ...") |
Open fails (metadata.rs:102) |
panic |
The open-fails arm is functionally indistinguishable from the unparseable-JSON arm — in both cases the cache is unusable and the obvious recovery is "carry on with an empty cache" — yet it is the only one that stops Selenium Manager entirely. The scenario that surfaced this (running Selenium inside a nono sandbox, reported in #18087) is exactly the case where degrading would let things work. Same applies to a cache file left root-owned by an earlier sudo run, or a read-only mount.
Proposed change
Treat an unopenable metadata file the same as an unparseable one:
let metadata_file = match File::open(&metadata_path) {
Ok(file) => file,
Err(err) => {
log.warn(format!(
"Metadata file {} cannot be opened: {}. Using empty metadata",
metadata_path.display(),
err
));
return new_metadata(log);
}
};Worth deciding at the same time whether the unparseable-JSON arm should warn rather than discard silently — right now a corrupt cache is invisible even at --debug.
Test gap
Neither graceful-degradation arm has any coverage. rust/tests/cache_unit_tests.rs exercises get_metadata/write_metadata round-trips against a tempdir(), but nothing drives a bad cache through get_metadata. The corrupt-JSON fallback at metadata.rs:110 is the cheap, portable one to pin:
#[test]
fn unparseable_metadata_falls_back_to_empty() {
let tmp = tempdir().unwrap();
let cache = tmp.path().to_path_buf();
fs::write(cache.join("se-metadata.json"), "{ not json").unwrap();
let meta = get_metadata(&Logger::default(), &Some(cache));
assert!(meta.drivers.is_empty() && meta.browsers.is_empty());
}A permission-denial equivalent is harder and lower value: 0o000 is readable by root, so the test would go red (not silently green) in root CI containers unless it self-guards, and there is no precedent for permission-denial tests in rust/ today — the only set_permissions in rust/tests is browser_tests.rs:254, granting 0o755. The corrupt-JSON test pins the same invariant without the portability cost.
Note
Separately, and not worth a change on its own: panic! introduced by #18087 is the first explicit panic in rust/src (grep -rn 'panic!(\|\.expect(' rust/src is otherwise empty). The crate's convention for fatal user-facing errors is log.error(...) + flush_and_exit(DATAERR, ...) in main.rs, which emits the JSON envelope the bindings parse; a panic exits 101 before log.flush() runs, so bindings surface the text as an unparsed dump. Adopting the fallback above makes this moot.
Source: SeleniumHQ/selenium