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

  1. Use a unique hookName per registration; encode the purpose, e.g. "message-received-log" and "message-received-metric"
  2. Deduplicate the slice of hook registrations before the loop
  3. If the hook must run twice, combine the actions into one registration
  4. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/a74dc991f8cc5f0c. Report an issue: GitHub.