{"record":{"id":"b775bfbe78702e16","repo":"hashicorp/nomad","slug":"one-of-launch-command-or-reattach-config-must-be-s","errorCode":null,"errorMessage":"one of launch command or reattach config must be specified","messagePattern":"one of launch command or reattach config must be specified","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/pluginutils/loader/loader.go","lineNumber":225,"sourceCode":"\n\treturn instance, nil\n}\n\n// Reattach reattaches to a previously launched external plugin.\nfunc (l *PluginLoader) Reattach(name, pluginType string, config *plugin.ReattachConfig) (PluginInstance, error) {\n\treturn l.dispensePlugin(pluginType, \"\", \"\", nil, config, l.logger)\n}\n\n// dispensePlugin is used to launch or reattach to an external plugin.\nfunc (l *PluginLoader) dispensePlugin(\n\tpluginType, apiVersion, cmd string, args []string, reattach *plugin.ReattachConfig,\n\tlogger log.Logger) (PluginInstance, error) {\n\n\tvar pluginCmd *exec.Cmd\n\tif cmd != \"\" && reattach != nil {\n\t\treturn nil, fmt.Errorf(\"both launch command and reattach config specified\")\n\t} else if cmd == \"\" && reattach == nil {\n\t\treturn nil, fmt.Errorf(\"one of launch command or reattach config must be specified\")\n\t} else if cmd != \"\" {\n\t\tpluginCmd = exec.Command(cmd, args...)\n\t}\n\n\tclient := plugin.NewClient(&plugin.ClientConfig{\n\t\tHandshakeConfig:  base.Handshake,\n\t\tPlugins:          getPluginMap(pluginType, logger),\n\t\tCmd:              pluginCmd,\n\t\tAllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},\n\t\tLogger:           logger,\n\t\tReattach:         reattach,\n\t})\n\n\t// Connect via RPC\n\trpcClient, err := client.Client()\n\tif err != nil {\n\t\tclient.Kill()\n\t\treturn nil, err","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/pluginutils/loader/loader.go#L207-L243","documentation":"The complementary guard in dispensePlugin: neither a launch command nor a reattach config was provided, so there is no way to obtain a plugin client. go-plugin needs either an exec.Cmd to spawn or a ReattachConfig to connect to an already-running process.","triggerScenarios":"Calling dispensePlugin with cmd == \"\" and reattach == nil — e.g. Dispense on a plugin whose exePath was never resolved (plugin discovered without a valid binary path), or Reattach called with a nil/empty reattach config.","commonSituations":"Plugin registry entry missing exePath because discovery failed silently; state restore passing an empty reattach config; tests constructing dispensePlugin calls with placeholder empty strings.","solutions":["Ensure the plugin's exePath is resolved (binary exists in plugin_dir) before Dispense.","When reattaching, build and pass a valid *plugin.ReattachConfig (with Protocol, Addr, Pid).","Re-run plugin discovery (NewPluginLoader/init) so the loader has a valid launch command.","Check state/reattach persistence for fields lost or zeroed on restore."],"exampleFix":"// before\ninstance, err := l.Reattach(nil) // nothing to reattach to\n// after\ninstance, err := l.Reattach(&plugin.ReattachConfig{\n  Protocol: plugin.ProtocolGRPC,\n  Addr:     addr,\n  Pid:      pid,\n})","handlingStrategy":"validation","validationCode":"func canDispense(cmd string, reattach *plugin.ReattachConfig) error {\n    if cmd == \"\" && reattach == nil {\n        return errors.New(\"need a launch command or a reattach config\")\n    }\n    return nil\n}\n// plus: ensure exePath resolved before Dispense\nif pinfo.exePath == \"\" && pinfo.factory == nil {\n    return errors.New(\"plugin discovered without executable path\")\n}","typeGuard":null,"tryCatchPattern":"instance, err := dispensePlugin(t, v, cmd, args, reattach, logger)\nif err != nil && strings.Contains(err.Error(), \"one of launch command or reattach config must be specified\") {\n    return fmt.Errorf(\"no launch info for plugin %s; re-run discovery\", t)\n}","preventionTips":["Verify plugin discovery resolves exePath before dispensing","Restore reattach configs fully (Protocol, Addr, Pid) from state","Fail fast at load time if a discovered plugin has no executable path"],"tags":["plugin-loader","argument-validation","reattach","nomad"],"backgroundTag":"missing-plugin-launch-options","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"}