{"record":{"id":"913f08beaf25a058","repo":"astral-sh/ruff","slug":"system-should-be-writable","errorCode":null,"errorMessage":"System should be writable","messagePattern":"System should be writable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ty_python_semantic/src/fixes.rs","lineNumber":92,"sourceCode":"const MAX_ITERATIONS: usize = 10;\n\n/// Applies all fixes for the given fix mode.\n///\n/// `check_file` is a separate parameter so that tests can easily mock out a file's diagnostics.\nfn fix_all<F>(\n    db: &mut dyn Db,\n    mut diagnostics: Vec<Diagnostic>,\n    fix_mode: FixMode,\n    cancellation_token: &CancellationToken,\n    check_file: F,\n) -> Result<FixAllResults, Canceled>\nwhere\n    F: Fn(&dyn Db, File) -> Vec<Diagnostic> + Sync,\n{\n    let system = WritableSystem::dyn_clone(\n        db.system()\n            .as_writable()\n            .expect(\"System should be writable\"),\n    );\n\n    let has_fixable = diagnostics\n        .iter()\n        .any(|diagnostic| fix_mode.is_fixable(diagnostic));\n\n    // Early return if there are no diagnostics that can be suppressed to avoid all the heavy work below.\n    if !has_fixable {\n        return Ok(FixAllResults {\n            diagnostics,\n            count: 0,\n        });\n    }\n\n    let mut by_file: BTreeMap<File, Vec<_>> = BTreeMap::new();\n\n    // Group the diagnostics by file, leave the file-agnostic diagnostics in `diagnostics`.\n    for diagnostic in diagnostics.extract_if(.., |diagnostic| diagnostic.primary_span().is_some()) {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/astral-sh/ruff/blob/d1087a4b9e03d253a88703f34e0869ee4b805456/crates/ty_python_semantic/src/fixes.rs#L74-L110","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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()"],"exampleFix":"// before\nlet results = fix_all(&mut db, diagnostics, fix_mode, &token, check_file)?;\n\n// after\nif db.system().as_writable().is_none() {\n    return Ok(FixAllResults { diagnostics, count: 0 });\n}\nlet results = fix_all(&mut db, diagnostics, fix_mode, &token, check_file)?;","handlingStrategy":"validation","validationCode":"if db.system().as_writable().is_none() {\n    return Ok(FixAllResults { diagnostics, count: 0 }); // check-only fallback\n}\nlet results = fix_all(&mut db, diagnostics, fix_mode, &token, check_file)?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["rust","ty","ruff-db","fixes","filesystem","system-abstraction"],"backgroundTag":"read-only-filesystem","analyzedSha":"d1087a4b9e03d253a88703f34e0869ee4b805456","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}