{"record":{"id":"468bb751856c6e0f","repo":"router-for-me/CLIProxyAPI","slug":"plugin-update-requires-restart","errorCode":"plugin_update_requires_restart","errorMessage":"loaded plugin library cannot be overwritten while the server is running","messagePattern":"loaded plugin library cannot be overwritten while the server is running","errorType":"error_code","errorClass":"ErrLoadedPluginLocked","httpStatus":409,"severity":"error","filePath":"internal/pluginstore/install.go","lineNumber":35,"sourceCode":"\tlog \"github.com/sirupsen/logrus\"\n)\n\ntype InstallOptions struct {\n\tPluginsDir string\n\tGOOS       string\n\tGOARCH     string\n\t// PluginLoaded reports whether the plugin's dynamic library is currently\n\t// loaded by the running host. Windows installs are rejected only when they\n\t// would overwrite an existing target file while it returns true.\n\tPluginLoaded func() bool\n\t// BeforeWrite runs after the archive has been downloaded and verified, but\n\t// before an existing target plugin file is replaced.\n\tBeforeWrite func() error\n}\n\n// ErrLoadedPluginLocked is returned when an install would overwrite a plugin\n// library that is loaded by the running process on Windows.\nvar ErrLoadedPluginLocked = errors.New(\"loaded plugin library cannot be overwritten while the server is running\")\n\ntype InstallResult struct {\n\tID          string `json:\"id\"`\n\tVersion     string `json:\"version\"`\n\tReleaseTag  string `json:\"release_tag,omitempty\"`\n\tInstallType string `json:\"install_type,omitempty\"`\n\tPath        string `json:\"path\"`\n\tOverwritten bool   `json:\"overwritten\"`\n\tSkipped     bool   `json:\"skipped\"`\n}\n\nfunc (c Client) Install(ctx context.Context, plugin Plugin, options InstallOptions) (InstallResult, error) {\n\tif errValidate := ValidatePlugin(plugin); errValidate != nil {\n\t\treturn InstallResult{}, errValidate\n\t}\n\toptions = normalizeInstallOptions(options)\n\tif PluginInstallType(plugin) == InstallTypeDirect {\n\t\tplugin.Version = normalizeVersion(plugin.Version)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/install.go#L17-L53","documentation":"Returned by pluginstore Install when an install would overwrite a plugin dynamic library that the running process currently has loaded, which Windows forbids (loaded DLLs are locked). The sentinel ErrLoadedPluginLocked carries the plugin_update_requires_restart code: the install must be retried after a restart unloads the library. On non-Windows platforms loaded libraries can be replaced on disk, so this primarily guards Windows deployments.","triggerScenarios":"POST to the plugin install/update management endpoint for a plugin whose PluginLoaded() returns true, on Windows, where the target .dll file still exists and would be overwritten by the extracted archive.","commonSituations":"Hot-updating a plugin through the management API on a Windows host; CI or operators scripting plugin updates against a live server.","solutions":["Restart the server (or unload the plugin if a unload endpoint exists) and re-run the same install request — the archive and ID stay valid","If you must update without downtime on Windows, move the deployment to a non-Windows host or accept the documented restart requirement","Check the InstallResult.Skipped/Overwritten fields and the plugin_update_requires_restart code to distinguish this from other install failures"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"res, err := pluginClient.Install(ctx, plugin, opts)\nif err != nil {\n    if errors.Is(err, pluginstore.ErrLoadedPluginLocked) {\n        // Windows: library file is locked; schedule reinstall after restart\n        schedulePostRestartInstall(plugin)\n        return reconciled(err)\n    }\n    return err\n}","preventionTips":["On Windows, treat plugin updates as restart-pending operations: queue them and re-apply on boot","Check InstallResult codes for plugin_update_requires_restart instead of retrying in a loop","Prefer non-Windows hosts for hot-reload plugin deployments"],"tags":["plugins","windows","install","restart-required","pluginstore"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}