astral-sh/ruff · error
System should be writable
Error message
System should be writable
What it means
fix_all in ty_python_semantic/src/fixes.rs mutates source files, so it requires the database's System to implement WritableSystem. System::as_writable (ruff_db/src/system.rs:221) returns None for read-only systems and the expect panics. RealSystem is always writable, so this panic is an API-embedding error (custom read-only System), not something the ty CLI itself produces on a normal filesystem.
Source
Thrown at crates/ty_python_semantic/src/fixes.rs:92
const MAX_ITERATIONS: usize = 10;
/// Applies all fixes for the given fix mode.
///
/// `check_file` is a separate parameter so that tests can easily mock out a file's diagnostics.
fn fix_all<F>(
db: &mut dyn Db,
mut diagnostics: Vec<Diagnostic>,
fix_mode: FixMode,
cancellation_token: &CancellationToken,
check_file: F,
) -> Result<FixAllResults, Canceled>
where
F: Fn(&dyn Db, File) -> Vec<Diagnostic> + Sync,
{
let system = WritableSystem::dyn_clone(
db.system()
.as_writable()
.expect("System should be writable"),
);
let has_fixable = diagnostics
.iter()
.any(|diagnostic| fix_mode.is_fixable(diagnostic));
// Early return if there are no diagnostics that can be suppressed to avoid all the heavy work below.
if !has_fixable {
return Ok(FixAllResults {
diagnostics,
count: 0,
});
}
let mut by_file: BTreeMap<File, Vec<_>> = BTreeMap::new();
// Group the diagnostics by file, leave the file-agnostic diagnostics in `diagnostics`.
for diagnostic in diagnostics.extract_if(.., |diagnostic| diagnostic.primary_span().is_some()) {View on GitHub (pinned to d1087a4b9e)
Solutions
- Construct the db with a writable system (RealSystem, or a TestSystem that implements WritableSystem) before calling fix-all
- If you implement System yourself, implement as_writable() to return Some along with the WritableSystem methods (write_file, create_new_file, ...)
- Gate the call: skip fix-all and return check-only results when db.system().as_writable().is_none()
Example fix
// before
let results = fix_all(&mut db, diagnostics, fix_mode, &token, check_file)?;
// after
if db.system().as_writable().is_none() {
return Ok(FixAllResults { diagnostics, count: 0 });
}
let results = fix_all(&mut db, diagnostics, fix_mode, &token, check_file)?; Defensive patterns
Strategy: validation
Validate before calling
if db.system().as_writable().is_none() {
return Ok(FixAllResults { diagnostics, count: 0 }); // check-only fallback
}
let results = fix_all(&mut db, diagnostics, fix_mode, &token, check_file)?; Prevention
- Build the Db with RealSystem (or a writable TestSystem) whenever your flow can apply fixes
- If you implement a custom System, implement as_writable() plus the WritableSystem methods rather than leaving the system read-only
- Gate fix-all entry points on writability so read-only sandboxes degrade to check-only instead of panicking
When it happens
Trigger: Invoking the fix-all path (`ty check --fix`, LSP fix-all) with a db whose system is a custom read-only System implementation that returns None from as_writable, e.g., an in-memory or virtual filesystem in a test harness or editor integration.
Common situations: Editor integrations and CI harnesses that build ty's Db with a sandboxed or virtual System but call the fix API; tests that reuse a read-only test system for fix-all cases.
Related errors
- should be set because `extract_if` only yields elements with
- extra use-def data should have been retained
- Should only ever pass a positive integer to `from_nonnegativ
- argument index should be valid
- Expected `NamedTuple` definition to be an assignment
AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20).
Data as JSON: /api/errors/913f08beaf25a058.
Report an issue: GitHub.