micro-editor/micro · warning
Cannot save readonly buffer
Error message
Cannot save readonly buffer
What it means
Returned by (*Buffer).saveToFile (internal/buffer/save.go:235) — the common backend of Save, SaveAs, SaveWithSudo — when b.Type.Readonly is true. This is about the buffer *type* (BTLog, and other internal readonly types like the help buffer family), not file permissions: type-level readonly buffers are viewer-only by design and every save variant refuses them.
Source
Thrown at internal/buffer/save.go:235
}
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
func (b *Buffer) SaveAs(filename string) error {
return b.saveToFile(filename, false, false)
}
func (b *Buffer) SaveWithSudo() error {
return b.SaveAsWithSudo(b.Path)
}
func (b *Buffer) SaveAsWithSudo(filename string) error {
return b.saveToFile(filename, true, false)
}
func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error {
var err error
if b.Type.Readonly {
return errors.New("Cannot save readonly buffer")
}
if b.Type.Scratch {
return errors.New("Cannot save scratch buffer")
}
if withSudo {
_, err := exec.LookPath(config.GlobalSettings["sucmd"].(string))
if err != nil {
return err
}
}
if !autoSave && b.Settings["rmtrailingws"].(bool) {
for i, l := range b.lines {
leftover := util.CharacterCount(bytes.TrimRightFunc(l.data, unicode.IsSpace))
linelen := util.CharacterCount(l.data)
b.Remove(Loc{leftover, i}, Loc{linelen, i})View on GitHub (pinned to 1c8b82b32e)
Solutions
- Select-all and copy the content, then open a normal buffer (Ctrl-t new / :open newfile), paste, and save that buffer
- If it's your own plugin buffer and saving should work, construct it with a writable BufType (BTDefault/BTScratch per intent) instead of a Readonly one
- Recognize the context: help/log panes are ephemeral viewers — nothing is lost by not saving
Example fix
// before (plugin creating a viewer buffer users then try to save) buf := buffer.NewBufferFromString(report, "report", buffer.BTLog) // after: make it a savable scratch/default buffer buf := buffer.NewBufferFromString(report, "report", buffer.BTDefault)
Defensive patterns
Strategy: validation
Validate before calling
// Only offer save for writable buffer types
func savable(b *buffer.Buffer) bool {
return !b.Type.Readonly && !b.Type.Scratch
}
if savable(h.Buf) {
h.Save()
} else {
InfoBar.Message("viewer buffer — copy content to a file buffer to save")
} Try / catch
if err := h.Buf.Save(); err != nil {
if err.Error() == "Cannot save readonly buffer" {
InfoBar.Error("this pane (help/log) is read-only; nothing was lost")
return // intentional design; do not retry with SaveAs — same check applies
}
return err
} Prevention
- Check focus before reflexive Ctrl-s: help and log panes cannot save
- Plugins: choose BufType by intent — viewers get Readonly, drafts get BTDefault
- SaveAs will not bypass this; only moving text to a normal buffer will
When it happens
Trigger: Pressing Ctrl-s or running :w / :save while focused on the > log pane, a help page, or any plugin-created buffer whose BufType has Readonly set; also SaveAs on such a buffer hits the same check because SaveAs routes through saveToFile.
Common situations: Users trying to annotate the log or help text and hitting save; plugins spawning readonly buffers for reports and offering a save keybinding that then errors.
Related errors
- Cannot save scratch buffer
- Unable to load help text for %s: %v
- Error: %s is a directory and cannot be saved
- Error: %s is not a regular file and cannot be saved
- Parent dirs don't exist, enable 'mkparents' for auto creatio
AI-assisted analysis of micro-editor/micro@1c8b82b32e (2026-08-15).
Data as JSON: /api/errors/09f8ed8a46587a25.
Report an issue: GitHub.