shadowsocks/shadowsocks-windows · error · ApplicationException

Plugin Program

Error message

Plugin Program

What it means

Thrown as ApplicationException wrapping a Win32Exception that occurred while starting the Sip003 plugin process, where the native error code is NOT 0x00000002 (file-not-found is handled separately). It is the catch-all for every other Process.Start failure: access denied, bad image, sharing violation, etc. The original Win32Exception is passed as the inner exception so the real native error is inspectable.

Solutions

  1. Inspect ex.NativeErrorCode of the inner Win32Exception to get the exact Windows error and act on it (e.g. 5 = access denied, 193 = bad exe format).
  2. For access denied, run the client with sufficient privileges or fix file ACLs.
  3. For bad exe format, use a plugin built for the matching architecture.
  4. Add an antivirus exclusion or stop the conflicting process that locks the file.

Example fix

// before
throw new ApplicationException(I18N.GetString("Plugin Program"), ex);

// after: include the native error code for diagnosis
throw new ApplicationException(
    I18N.GetString("Plugin Program") + $" (NativeError 0x{ex.NativeErrorCode:X8})", ex);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: can the account read/execute the plugin?
var fi = new FileInfo(resolvedPluginPath);
if (!fi.Exists) /* handle before start */

Type guard

static string ClassifyWin32(int code) => code switch
{
    2 => "file-not-found",
    5 => "access-denied",
    193 => "bad-exe-format",
    _ => "other"
};

Try / catch

try { plugin.Start(); }
catch (ApplicationException ex) when (ex.InnerException is Win32Exception w)
{ /* branch on w.NativeErrorCode: 5 perms, 193 arch, etc. */ }

Prevention

When it happens

Trigger: ERROR_ACCESS_DENIED (the user/account cannot execute the file or the file is locked); ERROR_BAD_EXE_FORMAT / 0xC1 (the plugin is not a valid executable for this architecture, e.g. 64-bit plugin on a 32-bit host); ERROR_SHARING_VIOLATION (the file is open exclusively elsewhere); the plugin requires elevation and UAC blocks the launch.

Common situations: Running the client as a normal user but the plugin requires admin; an architecture mismatch (x64 plugin vs x86 client); antivirus blocking execution; the plugin file being written/replaced while it is being started.

Related errors


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

Appendix: source

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

                _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)