Hook Kind Mismatch

Error: Hook "X" is registered as both a logic hook and a UI/action zone contribution.

The Problem

Two different modules (or two entries in the same manifest) register the same hook name, but the entries classify into two different kinds:

The Why

These are two disconnected virtual modules with different consumers. A hook name silently meaning two different things depending on which module registered it first means half the registrations are invisible to whichever consumer asked for the other kind — a runPipeline("X", ...) call would never see the UI-classified entries, and a <Hook zone="X"> would never see the logic-classified ones. No error, no warning — just a feature that silently doesn’t work for some of its contributors.

This mirrors launcher-trigger-collision and module-duplicate-id: the framework prefers a loud build-time failure over unpredictable, silently-partial behavior.

The Solution

  1. Rename one of the hooks. If the two features are genuinely distinct, give them different names.
  2. Match entry types. If they’re meant to be the same hook, make sure every registration classifies the same way — e.g. don’t mix a .astro UI entry and a .ts action entry under one name unless the name is launcher:-prefixed or :action-suffixed (which always forces UI/action classification).
  3. Avoid launcher:/:action naming for logic hooks. If you want runHook/runPipeline semantics, don’t name the hook launcher:* or *:action — those patterns always route to the UI/action pool regardless of entry type. See also hook-server-flag-ignored, the related trap this naming pattern creates.

Are you absolutely sure?

This action cannot be undone.