---
title: 5.2 Hook Kind Mismatch
permalink: 5-reference/hook-kind-mismatch
publish: true
visibility: public
tags:
  - manual
garden-url: https://francisfontaine.com/5-reference/hook-kind-mismatch
garden-short: https://stnd.gd/NPN2nY
created: 2026-07-29T19:39:12.625Z
modified: 2026-08-06T21:41:05.482Z
---

# 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:

- A **logic hook** — a `.js`/`.ts` entry whose name doesn't start with `launcher:` and doesn't end with `:action`. Compiled into `virtual:stnd/hooks`, called via `runHook()`/`runPipeline()`.
- A **UI/action zone** contribution — everything else (`.astro`/`.svelte` components, or any `launcher:`-prefixed / `:action`-suffixed entry regardless of file type). Compiled into `virtual:stnd/components`, rendered via `<Hook zone="...">` or read directly as `extensions[zone]`.

## 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](launcher-trigger-collision.md) and [module-duplicate-id](module-duplicate-id.md): 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](hook-server-flag-ignored.md), the related trap this naming pattern creates.

## Related Concepts

- [modules](modules.md): How the Standard module system works, including the full `hooks:` contract.
- [launcher-trigger-collision](launcher-trigger-collision.md): The same "fail loud on ambiguity" principle applied to launcher triggers.
