{"record":{"id":"52c800212c6a2326","repo":"golangci/golangci-lint","slug":"no-plugins-defined","errorCode":null,"errorMessage":"no plugins defined","messagePattern":"no plugins defined","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/commands/internal/configuration.go","lineNumber":43,"sourceCode":"\t// Destination is the path to a directory to store the binary.\n\tDestination string `yaml:\"destination,omitempty\"`\n\n\t// Plugins information.\n\tPlugins []*Plugin `yaml:\"plugins,omitempty\"`\n}\n\n// Validate checks and clean the configuration.\nfunc (c *Configuration) Validate() error {\n\tif strings.TrimSpace(c.Version) == \"\" {\n\t\treturn errors.New(\"root field 'version' is required\")\n\t}\n\n\tif strings.TrimSpace(c.Name) == \"\" {\n\t\tc.Name = defaultBinaryName\n\t}\n\n\tif len(c.Plugins) == 0 {\n\t\treturn errors.New(\"no plugins defined\")\n\t}\n\n\tfor _, plugin := range c.Plugins {\n\t\tif strings.TrimSpace(plugin.Module) == \"\" {\n\t\t\treturn errors.New(\"field 'module' is required\")\n\t\t}\n\n\t\tif strings.TrimSpace(plugin.Import) == \"\" {\n\t\t\tplugin.Import = plugin.Module\n\t\t}\n\n\t\tif strings.TrimSpace(plugin.Path) == \"\" && strings.TrimSpace(plugin.Version) == \"\" {\n\t\t\treturn errors.New(\"missing information: 'version' or 'path' should be provided\")\n\t\t}\n\n\t\tif strings.TrimSpace(plugin.Path) != \"\" && strings.TrimSpace(plugin.Version) != \"\" {\n\t\t\treturn errors.New(\"invalid configuration: 'version' and 'path' should not be provided at the same time\")\n\t\t}","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/golangci/golangci-lint/blob/ed7a235d2d771152056fdc142a9855567c5796a9/pkg/commands/internal/configuration.go#L25-L61","documentation":"`Configuration.Validate` (pkg/commands/internal/configuration.go:43) rejects a `.custom-gcl.*` config whose `plugins` list is empty or missing. The whole purpose of the custom golangci-lint builder is to compile one or more plugin modules into the binary, so a config without plugins is meaningless and validation stops here. Note the check is `len(c.Plugins) == 0`, so an empty list `plugins: []` also triggers it.","triggerScenarios":"Validating a `.custom-gcl.yml` that has `version` set but no `plugins` key, an empty `plugins:` key (decodes to nil), or an explicitly empty list `plugins: []`.","commonSituations":"Scaffolding the config file before adding plugins and running the build too early; commenting out all plugin entries; a YAML indentation mistake that puts plugins under another key so the root list stays empty.","solutions":["Add at least one entry under the root `plugins` key of the .custom-gcl config","Verify indentation so `plugins` is a root-level list, not nested or misnamed (e.g. `plugin:`)","If no plugins are actually needed, build the standard golangci-lint binary instead of using custom-gcl"],"exampleFix":"# before (.custom-gcl.yml)\nversion: v2.1.6\n# after\nversion: v2.1.6\nplugins:\n  - module: github.com/me/my-plugin\n    version: v1.0.0","handlingStrategy":"validation","validationCode":"data, _ := os.ReadFile(\".custom-gcl.yml\")\nvar raw struct {\n\tPlugins []map[string]any `yaml:\"plugins\"`\n}\n_ = yaml.Unmarshal(data, &raw)\nif len(raw.Plugins) == 0 {\n\t// add at least one plugin before running the build\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never commit a .custom-gcl config with an empty or missing plugins list","Check indentation so plugins entries are list items under the root `plugins` key","If no plugins are needed, use plain golangci-lint instead of custom-gcl"],"tags":["custom-gcl","config","plugins","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"ed7a235d2d771152056fdc142a9855567c5796a9","analyzedAt":"2026-09-02T18:47:33.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}