lapce/lapce · error
can't save to read only file
Error message
can't save to read only file
What it means
Raised by Buffer::save() in lapce-proxy/src/buffer.rs:67 when the buffer was constructed with read_only=true. Read-only buffers are Lapce's in-memory virtual documents (settings editor JSON, keymap views, welcome/preview panes, plugin-generated content) that are not backed by a writable file, so save() refuses before touching the filesystem or checking rev.
Source
Thrown at lapce-proxy/src/buffer.rs:67
};
let rope = Rope::from(s);
let rev = u64::from(!rope.is_empty());
let language_id = language_id_from_path(&path).unwrap_or("");
let mod_time = get_mod_time(&path);
Buffer {
id,
rope,
read_only,
path,
language_id,
rev,
mod_time,
}
}
pub fn save(&mut self, rev: u64, create_parents: bool) -> Result<()> {
if self.read_only {
return Err(anyhow!("can't save to read only file"));
}
if self.rev != rev {
return Err(anyhow!("not the right rev"));
}
let bak_extension = self.path.extension().map_or_else(
|| OsString::from("bak"),
|ext| {
let mut ext = ext.to_os_string();
ext.push(".bak");
ext
},
);
let path = if self.path.is_symlink() {
self.path.canonicalize()?
} else {
self.path.clone()
};View on GitHub (pinned to c9e4c33948)
Solutions
- Route read-only docs to Save As / a target-path dialog instead of save()
- Check the buffer's read_only flag before issuing the save RPC
- If the file should be editable, open the real file path instead of the virtual buffer
- Plugins: only call save on ids obtained from opened-file lifecycles, not virtual ones
Example fix
// before
buffer.save(rev, create_parents)?; // errors with "can't save to read only file" on virtual docs
// after
if buffer.read_only {
return Err(anyhow!("can't save to read only file: open a real path or use save-as"));
}
buffer.save(rev, create_parents)?; Defensive patterns
Strategy: validation
Validate before calling
// Before saving, branch on the buffer kind (Lapce proxy side)
if buffer.read_only {
// route to save-as with a target path instead of save()
return propose_save_as(buffer.id);
}
buffer.save(rev, create_parents)?; Type guard
fn is_writable_buffer(b: &Buffer) -> bool {
!b.read_only && b.path.as_ref().map_or(false, |p| p.is_absolute())
} Try / catch
match buffer.save(rev, create_parents) {
Err(e) if e.to_string().contains("read only") => propose_save_as(buffer.id),
other => other?,
} Prevention
- Check the read_only flag on every save path (commands, keymaps, plugin RPCs)
- Virtual documents should always be wired to save-as, never plain save
When it happens
Trigger: A save command (Ctrl+S / workspace save / plugin RPC 'save' with the buffer id) reaches a buffer whose read_only flag is set — e.g. the cursor sits in the settings editor or another virtual document and the keybinding routes to Buffer::save rather than a save-as flow.
Common situations: Plugins or editor commands calling document save on virtual buffer ids; older keybinding setups wiring plain save into the settings pane; UI flows that forgot to branch on document type.
Related errors
AI-assisted analysis of lapce/lapce@c9e4c33948 (2026-08-16).
Data as JSON: /api/errors/15abc08faceb5bce.
Report an issue: GitHub.