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

  1. Route read-only docs to Save As / a target-path dialog instead of save()
  2. Check the buffer's read_only flag before issuing the save RPC
  3. If the file should be editable, open the real file path instead of the virtual buffer
  4. 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

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.