deepseek-ai/deepseek-harness · error
${duplicateMessage}
Error message
${duplicateMessage} What it means
registerDefinition() is the shared keyed-registration path of the conversation Definition registries; ConversationEventRegistry.register keys each Definition by its kind, and the concrete message reads: conversation Definition "<kind>" is already registered. It throws when the key is already owned: the duplicate check runs immediately, before the effect installs the definition, so a second live registration of the same kind is always an error, never an overwrite. Registration is fiber-scoped and removed by the disposer only when the same definition still owns the key.
Source
Thrown at packages/client/runtime/src/client/conversation/definition-registry.ts:41
this.listeners.add(listener)
return () => { this.listeners.delete(listener) }
}
/**
* Register one uniquely keyed Definition for the caller's lifetime.
* @param key - registry-local unique key.
* @param definition - contributed Definition.
* @param duplicateMessage - error raised when the key is already owned.
* @param effectName - Cordis effect diagnostic label.
* @returns idempotent disposer.
*/
protected registerDefinition(
key: string,
definition: Definition,
duplicateMessage: string,
effectName: string,
): () => void {
if (this.definitions.has(key)) throw new Error(duplicateMessage)
const owner = this.ctx
const dispose = owner.effect(() => {
this.definitions.set(key, definition)
this.refresh()
return () => {
if (this.definitions.get(key) !== definition) return
this.definitions.delete(key)
this.refresh()
}
}, effectName)
return () => { void dispose() }
}
/** Refresh cached entries and synchronously invalidate subscribers. */
protected refresh(): void {
this.cached = [...this.definitions.values()]
for (const listener of this.listeners) listener()
}View on GitHub (pinned to b150a551b8)
Solutions
- Make the kind unique per business Definition — prefix by domain or owning package (e.g. 'tool.call.toolview' vs 'tool.call.chart').
- Check for double application: the same package mounted twice, or a fixture plus the real plugin both live — remove one or gate the fixture.
- Ensure disposers run before re-registration under HMR or repeated test setup: dispose the previous fiber/registration first.
Example fix
// before — two packages both claim 'tool.call'
events.register({ kind: 'tool.call', ... })
events.register({ kind: 'tool.call', ... }) // throws: already registered
// after — kinds are unique per owning package
events.register({ kind: 'tool.call.toolview', ... }) // ui-conversation
events.register({ kind: 'tool.call.chart', ... }) // ui-charts Defensive patterns
Strategy: validation
Validate before calling
const kindTaken = (events: ConversationEventRegistry, kind: string) =>
events.entries().some(d => d.kind === kind)
if (kindTaken(events, myKind)) throw new Error(`${myKind} already owned — pick a unique kind`)
events.register(definition) Prevention
- Derive the kind from the owning package/domain so collisions are structural, not accidental.
- Keep test fixtures on fixture-only kinds, never the production kind.
- Assert registration disposal in HMR-safety tests so re-apply never double-registers.
When it happens
Trigger: Two plugins (or the same plugin applied twice without disposal) call conversationEvents.register() with Definitions whose kind strings are equal; also HMR or repeated test setups where the previous fiber's disposer has not run before re-apply.
Common situations: Copying an existing UI plugin package and forgetting to rename its Definition kind; a test fixture registering a stub with the production kind while the real plugin is also composed; double ctx.plugin() of the same package in a hand-built harness.
Related errors
- conversation fallback Definition is already registered
- client-modules: ${pkgName} declares dsh.client but exports n
- conversation fallback Definition must declare a target
AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24).
Data as JSON: /api/errors/826d0961f091dd2a.
Report an issue: GitHub.