wtfutil/wtf · error

panic(err)

Error message

panic(err)

What it means

The todo widget's persist() method marshals the widget's todo list to YAML and writes it to disk with os.WriteFile. If the write fails for any reason (permissions, missing directory, disk full), the widget panics with the raw error via panic(err). This is the module's data-persistence path, so a failure means the todo list cannot be saved.

Source

Thrown at modules/todo/widget.go:285

		if err == nil {
			return text[5:], &date
		}
	}

	return text, nil
}

// persist writes the todo list to Yaml file
func (widget *Widget) persist() {
	confDir, _ := cfg.WtfConfigDir()
	filePath := fmt.Sprintf("%s/%s", confDir, widget.filePath)

	fileData, _ := yaml.Marshal(&widget.list)

	err := os.WriteFile(filePath, fileData, 0644)

	if err != nil {
		panic(err)
	}
}

// setItemChecks rolls through the checklist and ensures that all checklist
// items have the correct checked/unchecked icon per the user's preferences
func (widget *Widget) setItemChecks() {
	for _, item := range widget.list.Items {
		item.CheckedIcon = widget.settings.checked
		item.UncheckedIcon = widget.settings.unchecked
	}
}

// updateSelected sets the text of the currently-selected item to the provided text
func (widget *Widget) updateSelected() {
	if !widget.isItemSelected() {
		return
	}

View on GitHub (pinned to bb838c1ccb)

Solutions

  1. Verify the saveFilePath configured for the todo module exists and is writable by the user running wtfutil (e.g. `touch <filePath>` in a shell)
  2. Create the missing parent directory: `mkdir -p $(dirname <filePath>)`
  3. Fix ownership/permissions on the file or directory (chown/chmod) if a different user created it previously
  4. Check disk space (`df -h`) and read-only mount status if the path is on a network or special filesystem

Example fix

// before
err := os.WriteFile(filePath, fileData, 0644)
if err != nil {
    panic(err)
}
// after
if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
    log.Printf("todo: cannot create save dir: %v", err)
    return
}
if err := os.WriteFile(filePath, fileData, 0644); err != nil {
    log.Printf("todo: failed to persist list: %v", err)
}
Defensive patterns

Strategy: validation

Validate before calling

import "os"

func canPersist(path string) error {
    dir := filepath.Dir(path)
    if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
        return fmt.Errorf("save dir missing: %s", dir)
    }
    f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
    if err != nil {
        return err
    }
    f.Close()
    return nil
}

Try / catch

// wtfutil is a binary, not a library API; if embedding the widget code, recover around persist:
func safePersist(w *Widget, path string) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("todo persist failed: %v", r)
        }
    }()
    w.persist(path)
}

Prevention

When it happens

Trigger: Any call to persist() (from deleteSelected, demoteSelected, makeSelectedLast, promoteSelected, makeSelectedFirst, or toggleChecked) where os.WriteFile returns an error: the configured saveFilePath is in a non-existent directory, the file or directory is not writable by the current user, or the disk is full.

Common situations: Users configure a save file path under a directory that doesn't exist or was deleted; running wtfutil with different permissions than the user who created the file (root-created file, non-root reader); read-only filesystem or full disk; wrong path in the todo module config.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03). Data as JSON: /api/errors/161a6ee5ac8c0297. Report an issue: GitHub.