yorukot/superfile · error

cannot spawn process

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.

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.

Appendix: 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)