GopeedLab/gopeed · error

download directory is not in white list

Error message

download directory is not in white list

What it means

In Downloader.initOptions (pkg/download/downloader.go, ~1360-1389), after path placeholders are replaced, if the config has a non-empty WhiteDownloadDirs list the final opts.Path must match at least one entry via filepath.Match(dir, opts.Path), otherwise creation fails with 'download directory is not in white list'. This is a security feature confining downloads to approved directories.

Source

Thrown at pkg/download/downloader.go:1383

		if err != nil {
			return nil, err
		}
		opts.Path = storeConfig.DownloadDir
	}
	// Replace placeholders in download path (e.g., %year%, %month%, %day%, %date%)
	opts.Path = util.ReplacePathPlaceholders(opts.Path)

	// if enable white download directory, check if the download directory is in the white list
	if len(d.cfg.WhiteDownloadDirs) > 0 {
		inWhiteList := false
		for _, dir := range d.cfg.WhiteDownloadDirs {
			if match, err := filepath.Match(dir, opts.Path); match && err == nil {
				inWhiteList = true
				break
			}
		}
		if !inWhiteList {
			return nil, errors.New("download directory is not in white list")
		}
	}
	return opts, nil
}

func (d *Downloader) statusMut(task *Task, fn func() (bool, error)) (bool, error) {
	task.statusLock.Lock()
	defer task.statusLock.Unlock()

	return fn()
}

func (d *Downloader) doStart(task *Task) (err error) {
	var isCreate bool
	var generation uint64
	isReturn, err := d.statusMut(task, func() (isReturn bool, err error) {
		if task.Status == base.DownloadStatusRunning || task.Status == base.DownloadStatusDone {
			isReturn = true

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Set the task's opts.Path to a whitelisted directory (or leave it empty to use the configured default download dir, which is normally whitelisted)
  2. Fix the whitelist pattern to actually cover the path: use /data/downloads/* for one level or list each subdirectory; remember filepath.Match '*' does not cross '/'
  3. Verify with the same matcher before creating: filepath.Match(pattern, resolvedPath)
  4. For deployments, whitelist the base download directory and derive per-task subpaths under it

Example fix

// before
cfg.WhiteDownloadDirs = []string{"/srv/dl/*"}
_, err := d.CreateDirect(req, &base.Options{Path: "/srv/downloads/file.iso"}) // no match

// after
cfg.WhiteDownloadDirs = []string{"/srv/dl/*", "/srv/downloads/*"}
// or point the task inside an allowed dir:
_, err := d.CreateDirect(req, &base.Options{Path: "/srv/dl/file.iso"})
Defensive patterns

Strategy: validation

Validate before calling

func pathAllowed(whiteDirs []string, p string) bool {
    for _, d := range whiteDirs {
        if ok, err := filepath.Match(d, p); ok && err == nil { return true }
    }
    return false
}
resolved := util.ReplacePathPlaceholders(opts.Path)
if len(cfg.WhiteDownloadDirs) > 0 && !pathAllowed(cfg.WhiteDownloadDirs, resolved) {
    return fmt.Errorf("path %q not in white list", resolved)
}

Try / catch

taskId, err := downloader.CreateDirect(req, opts)
if err != nil && strings.Contains(err.Error(), "not in white list") {
    // show config guidance: whitelist the directory or choose an allowed path
}

Prevention

When it happens

Trigger: WhiteDownloadDirs is configured (e.g. ["/data/downloads/*"]) and a task is created with opts.Path = "/etc" or any non-matching directory; the path placeholder ({temp-dir} etc. via util.ReplacePathPlaceholders) resolves to a directory outside the whitelist; pattern semantics mismatch — filepath.Match treats '*' as not crossing '/' and has no '**', so /data/* won't match /data/a/b; case/separator differences (Windows \ vs /) between config and opts.Path.

Common situations: Server deployments enabling the whitelist but clients sending absolute paths; upgrading a deployment where the whitelist was added but old clients keep their previous paths; users assuming glob '**' support; relative paths in opts.Path that never match an absolute whitelist entry.

Related errors


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