shadowsocks/shadowsocks-windows · error · FileNotFoundException

Cannot find the plugin program file

Error message

Cannot find the plugin program file

What it means

Thrown as FileNotFoundException when Process.Start fails with a Win32Exception whose NativeErrorCode is 0x00000002 (ERROR_FILE_NOT_FOUND). It means the Sip003 plugin executable named in StartInfo.FileName could not be located on disk or resolved through the system PATH. The inner Win32Exception is preserved, and the message reports the resolved filename.

Source

Thrown at shadowsocks-csharp/Controller/Service/Sip003Plugin.cs:112

                var localPort = GetNextFreeTcpPort();
                LocalEndPoint = new IPEndPoint(IPAddress.Loopback, localPort);

                _pluginProcess.StartInfo.Environment["SS_LOCAL_HOST"] = LocalEndPoint.Address.ToString();
                _pluginProcess.StartInfo.Environment["SS_LOCAL_PORT"] = LocalEndPoint.Port.ToString();
                _pluginProcess.StartInfo.Arguments = ExpandEnvironmentVariables(_pluginProcess.StartInfo.Arguments, _pluginProcess.StartInfo.EnvironmentVariables);
                try
                {
                    _pluginProcess.Start();
                }
                catch (System.ComponentModel.Win32Exception ex)
                {
                    // do not use File.Exists(...), it can not handle the scenarios when the plugin file is in system environment path.
                    // https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
                    //if ((uint)ex.ErrorCode == 0x80004005)
                    //  https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d
                    if (ex.NativeErrorCode == 0x00000002)
                    {
                        throw new FileNotFoundException(I18N.GetString("Cannot find the plugin program file"), _pluginProcess.StartInfo.FileName, ex);
                    }
                    throw new ApplicationException(I18N.GetString("Plugin Program"), ex);
                }
                _pluginJob.AddProcess(_pluginProcess.Handle);
                _started = true;
            }

            return true;
        }

        public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null)
        {
            // Expand the environment variables from the new process itself
            if (environmentVariables != null)
            {
                foreach(string key in environmentVariables.Keys)
                {
                    name = name.Replace($"%{key}%", environmentVariables[key]);

View on GitHub (pinned to 891d971682)

Solutions

  1. Set the plugin command to an absolute path to the executable.
  2. Add the plugin's directory to the system PATH (and restart the app so it inherits the new PATH).
  3. Verify the file exists at the resolved location and the app has execute/read permission on it.
  4. Check the StartInfo.FileName actually being passed (environment variables may not be expanded).

Example fix

// before
_pluginProcess.StartInfo.FileName = "v2ray-plugin"; // not on PATH

// after
_pluginProcess.StartInfo.FileName = @"C:\Tools\v2ray-plugin\v2ray-plugin.exe";
Defensive patterns

Strategy: validation

Validate before calling

// Resolve the plugin path before starting
string resolved = ExpandEnvironmentVariables(plugin.Command);
if (!File.Exists(resolved) && !IsOnPath(resolved))
    throw new FileNotFoundException("Plugin not found", resolved);

Type guard

bool PluginPathLooksValid(string p) =>
    !string.IsNullOrWhiteSpace(p) &&
    (Path.IsPathRooted(p) || p.IndexOf(Path.DirectorySeparatorChar) >= 0 || IsOnPath(p));

Try / catch

try { plugin.Start(); }
catch (FileNotFoundException ex) when (ex.Message.Contains("plugin program file"))
{ /* tell user to set full path or install plugin on PATH */ }

Prevention

When it happens

Trigger: Plugin configured with a bare command name (e.g. "v2ray-plugin") that is neither in the working directory nor on PATH; a relative path that resolves against the wrong working directory; plugin file deleted/moved after config was written; PATH not inherited because the app was launched from a context with a restricted environment.

Common situations: User installs a plugin to a custom folder and only sets the filename without full path; portable distribution where the plugin is bundled but PATH is not set; running as a service under an account whose PATH differs from the interactive user; typo in the plugin command.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-windows@891d971682 (2026-08-13). Data as JSON: /api/errors/a4cd9b9d3ce78067. Report an issue: GitHub.