{"record":{"id":"f8a94d1594200e3c","repo":"canopy-network/canopy","slug":"plugin-launcher-not-found-for-q-checked-s","errorCode":null,"errorMessage":"plugin launcher not found for %q; checked: %s","messagePattern":"plugin launcher not found for %q; checked: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"controller/controller.go","lineNumber":402,"sourceCode":"\t\tcandidates = append(candidates,\n\t\t\tfilepath.Join(exeDir, relPath),\n\t\t\tfilepath.Join(filepath.Dir(exeDir), relPath),\n\t\t)\n\t}\n\t// add a path relative to the source tree for local development\n\tif _, sourceFile, _, ok := runtime.Caller(0); ok {\n\t\trepoRoot := filepath.Dir(filepath.Dir(sourceFile))\n\t\tcandidates = append(candidates, filepath.Join(repoRoot, relPath))\n\t}\n\t// return the first existing file path\n\tfor _, candidate := range candidates {\n\t\tinfo, err := os.Stat(candidate)\n\t\tif err == nil && !info.IsDir() {\n\t\t\treturn candidate, nil\n\t\t}\n\t}\n\t// exit with a descriptive error containing all attempted paths\n\treturn \"\", fmt.Errorf(\"plugin launcher not found for %q; checked: %s\", plugin, strings.Join(candidates, \", \"))\n}\n\n// INTERNAL CALLS BELOW\n\n// LoadIsOwnRoot() returns if this chain is its own root (base)\nfunc (c *Controller) LoadIsOwnRoot() (isOwnRoot bool) {\n\t// use the state machine to check if this chain is the root chain\n\tisOwnRoot, err := c.FSM.LoadIsOwnRoot()\n\t// if an error occurred\n\tif err != nil {\n\t\t// log the error\n\t\tc.log.Error(err.Error())\n\t}\n\t// exit\n\treturn\n}\n\n// RootChainId() returns the root chain id according to the FSM","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/controller/controller.go#L384-L420","documentation":"resolvePluginCtlPath searches known locations (working directory, executable-relative paths, and the source tree) for plugin/<name>/pluginctl.sh and returns this error when none of the candidates exists as a regular file. The message lists every path attempted so the missing location is diagnosable.","triggerScenarios":"runPluginCtl invoking a plugin whose pluginctl.sh is not installed in any of the checked locations: ./plugin/<plugin>/pluginctl.sh, relative to the executable dir, its parent, or the repo root.","commonSituations":"Running a compiled binary outside the repo without copying the plugin directory, running from the wrong working directory, a plugin name typo, or an incomplete build/install that omitted the plugin assets.","solutions":["Check the 'checked:' path list in the error and create/copy pluginctl.sh at one of those exact locations","Run the binary from the repository root where ./plugin/<name>/pluginctl.sh exists","Verify the plugin name is spelled correctly (it maps to a plugin subdirectory)","Install the plugin assets next to the executable (exe_dir/plugin/<name>/pluginctl.sh) for deployed binaries"],"exampleFix":"// before\n$ ./bin/canopyd plugin-enable indexer   # run from $HOME\n// after\n$ cd /path/to/repo && ./bin/canopyd plugin-enable indexer\n# or install assets:\n$ mkdir -p bin/plugin/indexer && cp plugin/indexer/pluginctl.sh bin/plugin/indexer/","handlingStrategy":"validation","validationCode":"for _, dir := range candidateDirs() {\n    p := filepath.Join(dir, \"plugin\", pluginName, \"pluginctl.sh\")\n    if info, err := os.Stat(p); err == nil && !info.IsDir() { return p, nil }\n}\nreturn \"\", fmt.Errorf(\"pluginctl.sh for %q missing; run make install-plugins\", pluginName)","typeGuard":null,"tryCatchPattern":"path, err := resolvePluginCtlPath(plugin)\nif err != nil {\n    log.Fatalf(\"plugin install problem: %v\", err) // error lists all checked paths\n}","preventionTips":["Install plugin assets alongside the binary at deploy time","Run daemons from the repo root in dev","Validate plugin names against a known list before invoking"],"tags":["plugin","file-not-found","config","deployment"],"backgroundTag":"file-not-found","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}