router-for-me/CLIProxyAPI · error

zip entry %s uses backslash path separators

Error message

zip entry %s uses backslash path separators

What it means

Returned by cleanZipName (install.go:379-381) when an entry name contains a backslash. The zip spec mandates forward slashes; backslashes are ambiguous (a legal filename character on Unix, a separator on Windows) and are a classic zip-slip vector, so the store rejects them outright instead of guessing.

Source

Thrown at internal/pluginstore/install.go:380

		return nil, 0, fmt.Errorf("read %s: %w", targetName, errRead)
	}
	mode := target.FileInfo().Mode().Perm()
	if mode == 0 {
		mode = 0o755
	}
	return data, mode, nil
}

func versionedPluginFileName(id string, version string, goos string) string {
	return strings.TrimSpace(id) + "-v" + normalizeVersion(version) + pluginExtension(goos)
}

func cleanZipName(name string) (string, error) {
	if strings.TrimSpace(name) == "" {
		return "", fmt.Errorf("zip entry has empty name")
	}
	if strings.Contains(name, `\`) {
		return "", fmt.Errorf("zip entry %s uses backslash path separators", name)
	}
	if path.IsAbs(name) {
		return "", fmt.Errorf("zip entry %s is absolute", name)
	}
	cleaned := path.Clean(name)
	if cleaned == "." || cleaned == ".." || strings.HasPrefix(cleaned, "../") {
		return "", fmt.Errorf("zip entry %s escapes archive root", name)
	}
	return cleaned, nil
}

func regularZipFile(file *zip.File) bool {
	mode := file.FileInfo().Mode()
	return mode.IsRegular() || mode.Type() == 0
}

func hasDynamicLibraryExtension(name string) bool {
	lowerName := strings.ToLower(name)

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Rebuild the archive with forward-slash paths (e.g. use Go's archive/zip, Info-ZIP, or 'tar' equivalents)
  2. If stuck with the original producer, re-zip on a system that normalizes separators: unzip then zip fresh
  3. Fix the release pipeline to use a spec-compliant zip library

Example fix

# before (PowerShell, stores backslashes)
Compress-Archive -Path bin\myplugin.dll -DestinationPath plugin.zip
# after (forward slashes, spec compliant)
zip plugin.zip bin/myplugin.dll
Defensive patterns

Strategy: validation

Validate before calling

func zipUsesForwardSlashes(archiveData []byte) error {
    r, err := zip.NewReader(bytes.NewReader(archiveData), int64(len(archiveData)))
    if err != nil { return err }
    for _, f := range r.File {
        if strings.Contains(f.Name, `\`) {
            return fmt.Errorf("entry %q uses backslash separators", f.Name)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: InstallArchive on a zip written by a Windows tool that stored paths like 'bin\\myplugin.dll'. Any entry in the archive triggers it — the check runs for all entries before extension filtering, not just the target library.

Common situations: Artifacts zipped with PowerShell's Compress-Archive or .NET System.IO.Compression in some configurations; older Windows packers that stored native separators.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/2b4f5d4f95f92b7f. Report an issue: GitHub.