{"record":{"id":"b92f902ebafee0a5","repo":"hashicorp/nomad","slug":"w-q-wraps-errpluginnotexists","errorCode":null,"errorMessage":"%w: %q (wraps ErrPluginNotExists)","messagePattern":"%w: %q \\(wraps ErrPluginNotExists\\)","errorType":"exception","errorClass":"ErrPluginNotExists","httpStatus":null,"severity":"error","filePath":"client/commonplugins/secrets_plugin.go","lineNumber":59,"sourceCode":"\tlogger log.Logger\n\n\t// pluginPath is the path on the host to the plugin executable\n\tpluginPath string\n}\n\n// NewExternalSecretsPlugin creates an instance of a secrets plugin by validating the plugin\n// binary exists and is executable, and parsing any string key/value pairs out of the config\n// which will be used as environment variables for Fetch.\nfunc NewExternalSecretsPlugin(commonPluginDir string, name string) (*externalSecretsPlugin, error) {\n\t// validate plugin\n\tif runtime.GOOS == \"windows\" {\n\t\tname += \".exe\"\n\t}\n\texecutable := filepath.Join(commonPluginDir, SecretsPluginDir, name)\n\tf, err := os.Stat(executable)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, fmt.Errorf(\"%w: %q\", ErrPluginNotExists, name)\n\t\t}\n\t\treturn nil, err\n\t}\n\tif !helper.IsExecutable(f) {\n\t\treturn nil, fmt.Errorf(\"%w: %q\", ErrPluginNotExecutable, name)\n\t}\n\n\treturn &externalSecretsPlugin{pluginPath: executable}, nil\n}\n\nfunc (e *externalSecretsPlugin) Fingerprint(ctx context.Context) (*PluginFingerprint, error) {\n\tplugCtx, cancel := context.WithTimeout(ctx, SecretsCmdTimeout)\n\tdefer cancel()\n\n\tcmd := exec.CommandContext(plugCtx, e.pluginPath, \"fingerprint\")\n\tcmd.Env = []string{\n\t\t\"CPI_OPERATION=fingerprint\",\n\t}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/commonplugins/secrets_plugin.go#L41-L77","documentation":"`NewExternalSecretsPlugin` in the Nomad client's commonplugins package looks for a secrets plugin binary under commonPluginDir/SecretsPluginDir, appending .exe on Windows. If os.Stat reports the file does not exist, it returns ErrPluginNotExists wrapped with the plugin binary name. Nomad uses errors.Is/As against ErrPluginNotExists to distinguish \"plugin absent\" from real failures.","triggerScenarios":"Fingerprinting or building secret providers when the expected secrets plugin executable is not installed in the plugin directory (e.g. plugins/secrets/ dir under the configured plugin_dir) or the plugin name is misspelled.","commonSituations":"Fresh Nomad install where the secrets plugin was never deployed; plugin deployed to the wrong directory; plugin binary name doesn't match the expected name; operators referencing a plugin only available on some nodes.","solutions":["Install the secrets plugin binary into <plugin_dir>/<SecretsPluginDir>/ with the expected name","Verify the plugin name/path in your agent config matches the actual file on disk","If the plugin is optional, handle errors.Is(err, commonplugins.ErrPluginNotExists) by skipping instead of failing","Confirm correct binary name for the OS (no .exe suffix needed on Linux, required on Windows)"],"exampleFix":"// handling code\n// before\np, err := commonplugins.NewExternalSecretsPlugin(logger, name, dir)\nif err != nil { return err }\n// after\np, err := commonplugins.NewExternalSecretsPlugin(logger, name, dir)\nif errors.Is(err, commonplugins.ErrPluginNotExists) {\n    logger.Warn(\"secrets plugin not installed, skipping\", \"plugin\", name)\n    return nil\n} else if err != nil {\n    return err\n}","handlingStrategy":"type-guard","validationCode":"p := filepath.Join(pluginDir, \"secrets\", pluginName)\nif _, err := os.Stat(p); os.IsNotExist(err) {\n    log.Fatalf(\"secrets plugin %q not installed at %s\", pluginName, p)\n}","typeGuard":"func isPluginNotExists(err error) bool {\n    return errors.Is(err, commonplugins.ErrPluginNotExists)\n}","tryCatchPattern":"p, err := commonplugins.NewExternalSecretsPlugin(logger, name, dir)\nswitch {\ncase errors.Is(err, commonplugins.ErrPluginNotExists):\n    return nil // plugin optional; skip\ncase err != nil:\n    return err\n}","preventionTips":["Deploy the secrets plugin binary to the correct plugin_dir subdirectory with the expected name","Check plugin presence in node provisioning before registering the client","Use errors.Is against ErrPluginNotExists to treat absence as optional"],"tags":["nomad","plugin","secrets","file-not-found"],"backgroundTag":"plugin-not-found","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}