larksuite/cli · error
%s %q: hookName already used in this plugin
Error message
%s %q: hookName already used in this plugin
What it means
validateHookName rejects duplicate hook names within one plugin Builder; each hookName may be registered at most once across Observer, Wrap, and On. The duplicate action is skipped and the error is stored on the Builder.
Source
Thrown at extension/platform/builder.go:244
// behaviour: a misconfigured plugin must NOT be silently registered.
func (b *Builder) MustBuild() Plugin {
p, err := b.Build()
if err != nil {
panic(fmt.Sprintf("plugin %q: %v", b.name, err))
}
return p
}
// validateHookName checks the grammar and uniqueness; returns false
// when the name was rejected (caller skips the action).
func (b *Builder) validateHookName(hookName, kind string) bool {
if !pluginNamePattern.MatchString(hookName) {
b.errs = append(b.errs, fmt.Errorf(
"%s %q: hookName must match ^[a-z0-9][a-z0-9-]*$", kind, hookName))
return false
}
if b.hookNames[hookName] {
b.errs = append(b.errs, fmt.Errorf(
"%s %q: hookName already used in this plugin", kind, hookName))
return false
}
b.hookNames[hookName] = true
return true
}
// builtPlugin is the Plugin implementation the builder emits.
type builtPlugin struct {
name string
version string
caps Capabilities
actions []func(Registrar)
rules []*Rule
skillsOverlay *SkillsOverlay
}
func (p *builtPlugin) Name() string { return p.name }View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Use a unique hookName per registration; encode the purpose, e.g. "message-received-log" and "message-received-metric"
- Deduplicate the slice of hook registrations before the loop
- If the hook must run twice, combine the actions into one registration
- Check b.hookNames/Builder errors after build to find which name collided
Example fix
// before
b.On("message-received", logHook)
b.On("message-received", metricHook)
// after
b.On("message-received-log", logHook)
b.On("message-received-metric", metricHook) Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]bool{}
for _, h := range hooks {
if seen[h.name] { t.Fatalf("duplicate hook %q", h.name) }
seen[h.name] = true
} Try / catch
// duplicates are recorded on the Builder and actions skipped:
if errs := b.Errs(); len(errs) > 0 { return fmt.Errorf("plugin build failed: %v", errs) } Prevention
- Deduplicate registration slices before looping
- Compose multiple behaviors into one hook instead of re-registering
- Assert in tests that each Builder accumulates zero errors
- Generate hook names with a suffix derived from purpose
When it happens
Trigger: Registering the same hookName twice on one Builder, e.g. calling b.On("message-received", h1) then b.On("message-received", h2), or registering Observer and Wrap with the same name.
Common situations: Copy-pasted registration blocks; looping over a slice containing duplicate hook names; merging two plugin configurations into one Builder without deduplication.
Related errors
- %s %q: hookName must match ^[a-z0-9][a-z0-9-]*$
- invalid plugin name %q: must match ^[a-z0-9][a-z0-9-]*$
- invalid identity %q: must be user|bot
- invalid risk %q: must be read|write|high-risk-write
- hook %q panic: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/a74dc991f8cc5f0c.
Report an issue: GitHub.