GopeedLab/gopeed · warning

script file does not exist: %s

Error message

script file does not exist: %s

What it means

Returned by executeScriptAtPath (script.go:57-65) when a configured script hook path does not exist on disk at execution time. Scripts are fired asynchronously after download events (triggerScripts spawns executeScripts), so the failure is not raised to the download flow; it is logged as a warning with the offending path (script.go:155). The check uses os.Stat with IsNotExist, so any missing path (typo, moved file, wrong working directory) triggers it.

Source

Thrown at pkg/download/script.go:64

		}
		if len(paths) > 0 {
			return paths
		}
	}

	return nil
}

// executeScriptAtPath executes a single script with the given data
// Returns any error that occurred during execution
func (d *Downloader) executeScriptAtPath(scriptPath string, data *ScriptData) error {
	if scriptPath == "" {
		return fmt.Errorf("script path is empty")
	}

	// Check if script file exists
	if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
		return fmt.Errorf("script file does not exist: %s", scriptPath)
	}

	// Determine the script interpreter based on file extension
	var cmd *exec.Cmd
	ext := filepath.Ext(scriptPath)
	
	switch ext {
	case ".sh", ".bash":
		cmd = exec.Command("bash", scriptPath)
	case ".py":
		cmd = exec.Command("python3", scriptPath)
	case ".js":
		cmd = exec.Command("node", scriptPath)
	case ".bat", ".cmd":
		// Windows batch files
		if runtime.GOOS == "windows" {
			cmd = exec.Command("cmd", "/c", scriptPath)
		} else {

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Correct the path in the script settings to an existing file
  2. Prefer absolute paths over relative ones so resolution does not depend on process CWD
  3. For containers, mount the script into the container and reference the in-container path
  4. Check the gopeed log for the 'script: failed to execute' warning to see the exact failing path

Example fix

# before (config)
"script": { "enable": true, "paths": ["notify.sh"] }   # relative, CWD-dependent

# after
"script": { "enable": true, "paths": ["/opt/gopeed/scripts/notify.sh"] }   # absolute, mounted in container
Defensive patterns

Strategy: validation

Validate before calling

for _, p := range cfg.Script.Paths {
	if _, err := os.Stat(p); err != nil {
		return fmt.Errorf("script hook %q not found", p)
	}
}

Try / catch

if err := d.executeScriptAtPath(path, data); err != nil {
	log.Warn("script hook failed", "path", path, "err", err)
}

Prevention

When it happens

Trigger: DownloaderStoreConfig.Script.Paths contains a path that does not exist when DOWNLOAD_DONE/DOWNLOAD_ERROR fires: a typo, a file deleted after the config was saved, a relative path resolved against a different CWD (desktop app vs systemd/docker service), or a config synced from another machine.

Common situations: Absolute home-directory paths like /home/user/notify.sh that do not exist in a Docker deployment; scripts moved during refactoring without updating settings; portable installs where the config travels but the script does not; NAS environments with different mount points.

Related errors


AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16). Data as JSON: /api/errors/553e562b22e09c04. Report an issue: GitHub.