yorukot/superfile · error

cannot spawn process : %w

Error message

cannot spawn process : %w

What it means

zipSources wraps a failure from processBar.SendAddProcessMsg when it tries to register a new compression process entry in the progress-bar model. The compression never starts because the progress tracking infrastructure could not spawn the process record.

Source

Thrown at src/internal/file_operations_compress.go:91

		totalFiles, err := validateCompressOperation(filesToCompress)
		if err != nil {
			return NewNotifyModalMsg(notify.New(true, "Invalid file/dir to compress", err.Error(), notify.NoAction),
				reqID)
		}
		if err := zipSources(filesToCompress, totalFiles, zipPath, &m.processBarModel); err != nil {
			slog.Error("Error in zipping files", "error", err)
			return NewCompressOperationMsg(processbar.Failed, reqID)
		}
		return NewCompressOperationMsg(processbar.Successful, reqID)
	}
}

func zipSources(sources []string, totalFiles int, target string, processBar *processbar.Model) error {
	var err error

	p, err := processBar.SendAddProcessMsg(filepath.Base(target), processbar.OpCompress, totalFiles, true)
	if err != nil {
		return fmt.Errorf("cannot spawn process : %w", err)
	}
	_, err = os.Stat(target)
	if err == nil {
		p.ErrorMsg = "File already exists"
		p.State = processbar.Cancelled
		p.DoneTime = time.Now()
		pSendErr := processBar.SendUpdateProcessMsg(p, true)
		if pSendErr != nil {
			slog.Error("Error sending process update", "error", pSendErr)
		}
		return errors.New("file already exists")
	}

	f, err := os.Create(target)
	if err != nil {
		return err
	}
	defer f.Close()

View on GitHub (pinned to b72f550bc6)

Solutions

  1. Ensure the processbar.Model is initialized and its event loop is running before calling zipSources.
  2. Check application shutdown ordering: cancel compressions before closing the TUI.
  3. Recover the wrapped cause with errors.Unwrap/errors.As to see the underlying send failure.
  4. Retry the operation with a fresh processbar.Model if the previous one was closed.

Example fix

// before
p, err := processBar.SendAddProcessMsg(filepath.Base(target), processbar.OpCompress, totalFiles, true)
if err != nil {
    return fmt.Errorf("cannot spawn process : %w", err)
}
// after
select {
case <-done: // UI shutting down
    return fmt.Errorf("compression cancelled: UI shutting down")
default:
}
p, err := processBar.SendAddProcessMsg(filepath.Base(target), processbar.OpCompress, totalFiles, true)
if err != nil {
    return fmt.Errorf("cannot spawn process : %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the progress model is alive before starting
if processBar == nil || shuttingDown {
    return fmt.Errorf("progress UI not available")
}

Try / catch

if err := ops.ZipSources(sources, total, target, processBar); err != nil {
    if strings.Contains(err.Error(), "cannot spawn process") {
        // recreate processbar.Model and retry, or run without progress UI
    }
    return err
}

Prevention

When it happens

Trigger: Calling zipSources with a processbar.Model whose message channel is closed/unavailable, the TUI is shutting down, or the internal message send fails (e.g. model not initialized or already terminated).

Common situations: Starting a compression while the UI is exiting; reusing a finished/closed processbar.Model; race between UI teardown and a queued compression job.

Related errors


AI-assisted analysis of yorukot/superfile@b72f550bc6 (2026-09-01). Data as JSON: /api/errors/6b06920ecfda9e07. Report an issue: GitHub.