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

  1. Select-all and copy the content, then open a normal buffer (Ctrl-t new / :open newfile), paste, and save that buffer
  2. 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
  3. 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

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


AI-assisted analysis of micro-editor/micro@1c8b82b32e (2026-08-15). Data as JSON: /api/errors/09f8ed8a46587a25. Report an issue: GitHub.