{"record":{"id":"d99862e0d1cf5985","repo":"larksuite/cli","slug":"invalid-plugin-name-q-must-match-a-z0-9-a-z0","errorCode":null,"errorMessage":"invalid plugin name %q: must match ^[a-z0-9][a-z0-9-]*$","messagePattern":"invalid plugin name %q: must match \\^\\[a-z0-9\\]\\[a-z0-9-\\]\\*\\$","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"extension/platform/builder.go","lineNumber":62,"sourceCode":"\tskillsOverlay *SkillsOverlay\n\n\thookNames map[string]bool\n\terrs      []error\n}\n\nvar pluginNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)\n\n// NewPlugin starts a Builder. Name format is validated lazily — errors\n// surface at Build()/MustBuild() time, allowing chained calls without\n// intermediate error handling.\nfunc NewPlugin(name, version string) *Builder {\n\tb := &Builder{\n\t\tname:      name,\n\t\tversion:   version,\n\t\thookNames: map[string]bool{},\n\t}\n\tif !pluginNamePattern.MatchString(name) {\n\t\tb.errs = append(b.errs, fmt.Errorf(\"invalid plugin name %q: must match ^[a-z0-9][a-z0-9-]*$\", name))\n\t}\n\treturn b\n}\n\n// RequireCLI sets Capabilities.RequiredCLIVersion (semver constraint,\n// e.g. \">=1.1.0\"). Empty string means no requirement.\nfunc (b *Builder) RequireCLI(constraint string) *Builder {\n\tb.caps.RequiredCLIVersion = constraint\n\treturn b\n}\n\n// FailOpen sets Capabilities.FailurePolicy = FailOpen. Default when\n// neither FailOpen nor FailClosed is called and neither Restrict nor\n// EmbeddedSkills is used. Build rejects a final FailOpen state after either\n// safety-sensitive contribution.\nfunc (b *Builder) FailOpen() *Builder {\n\tb.caps.FailurePolicy = FailOpen\n\treturn b","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/extension/platform/builder.go#L44-L80","documentation":"NewPlugin in extension/platform validates the plugin name against ^[a-z0-9][a-z0-9-]*$ and records this error on the Builder. The name must start with a lowercase letter or digit and contain only lowercase alphanumerics and hyphens.","triggerScenarios":"Calling NewPlugin with a name containing uppercase letters, underscores, spaces, or starting with a hyphen, e.g. NewPlugin(\"My_Plugin\", \"1.0.0\").","commonSituations":"Porting plugin names from camelCase or snake_case conventions; names derived from directory or Go package names; typos or copied product names with spaces.","solutions":["Rename the plugin to lowercase kebab-case, e.g. \"my-plugin\"","Normalize the name before calling NewPlugin: lowercase and replace '_'/' ' with '-'","Check that the name does not start with '-' or contain consecutive invalid characters","Read the error's %q value to see the exact offending string"],"exampleFix":"// before\nb := platform.NewPlugin(\"My_Plugin\", \"1.0.0\")\n// after\nb := platform.NewPlugin(\"my-plugin\", \"1.0.0\")","handlingStrategy":"validation","validationCode":"var pluginNameRe = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)\nif !pluginNameRe.MatchString(name) { return fmt.Errorf(\"bad plugin name %q\", name) }","typeGuard":null,"tryCatchPattern":"b := platform.NewPlugin(name, ver)\nif len(b.Errs()) > 0 { /* inspect b.Errs() for the name error before building */ }","preventionTips":["Normalize names: strings.ToLower + replace non [a-z0-9-] with '-'","Lint plugin manifests for name pattern in CI","Never derive plugin names from Go identifiers directly","Keep a single slugify helper for plugin and hook names"],"tags":["plugin","naming","validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}