{"record":{"id":"36135cd13e7ba4f7","repo":"hashicorp/nomad","slug":"both-launch-command-and-reattach-config-specified","errorCode":null,"errorMessage":"both launch command and reattach config specified","messagePattern":"both launch command and reattach config specified","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"helper/pluginutils/loader/loader.go","lineNumber":223,"sourceCode":"\t\treturn nil, fmt.Errorf(\"setting config for plugin %s failed: %v\", id, err)\n\t}\n\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 {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/helper/pluginutils/loader/loader.go#L205-L241","documentation":"dispensePlugin supports exactly one launch mode: spawn a command OR reattach to an existing process. Supplying both a non-empty cmd and a non-nil reattach config is contradictory and rejected with this error. It guards against ambiguous process ownership (killFn would be unclear).","triggerScenarios":"Calling dispensePlugin with both cmd != \"\" and reattach != nil. From public APIs this happens if Dispense is combined with reattach logic or Reattach is called with a non-empty cmd derived from stored plugin info plus a reattach config — usually a bug in caller wiring, not user config.","commonSituations":"Reattach plumbing bugs where a persisted plugin command is passed alongside a ReattachConfig; tests hand-crafting dispensePlugin arguments; custom integrations resuming from state files that store both fields.","solutions":["Pass exactly one of cmd or reattach to dispensePlugin; nil the other.","When reattaching, pass reattach config with an empty cmd string.","Fix state-persistence code so it records either launch info or reattach info, not both.","Audit callers (Dispense, Reattach, anonymous factories) for accidental double-population of arguments."],"exampleFix":"// before\ndispensePlugin(\"driver\", ver, exePath, args, reattachCfg, logger) // both set\n// after\ndispensePlugin(\"driver\", ver, \"\", nil, reattachCfg, logger)","handlingStrategy":"validation","validationCode":"func validDispenseArgs(cmd string, reattach *plugin.ReattachConfig) error {\n    if cmd != \"\" && reattach != nil {\n        return errors.New(\"pass either cmd or reattach, not both\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := dispensePlugin(pType, ver, exePath, args, reattachCfg, logger); err != nil {\n    if strings.Contains(err.Error(), \"both launch command and reattach\") {\n        return fmt.Errorf(\"caller bug: clear one launch mode: %w\", err)\n    }\n    return err\n}","preventionTips":["Structure callers so launch and reattach modes are mutually exclusive branches","Persist either launch info or reattach info in state, never both","Add unit tests covering each mode combination"],"tags":["plugin-loader","argument-validation","reattach","nomad"],"backgroundTag":"conflicting-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"}