deepseek-ai/deepseek-harness · error
conversation fallback Definition is already registered
Error message
conversation fallback Definition is already registered
What it means
The fallback slot is single-occupancy by design: exactly one Definition handles events that no keyed Definition matched. registerFallback() throws when a fallback is already installed — a second registration, even with a different kind, is refused rather than overriding. The disposer frees the slot, and only if the current fallback is still the same object.
Source
Thrown at packages/client/runtime/src/client/conversation/event-registry.ts:38
assertDefinitionTarget(definition)
return this.registerDefinition(
definition.kind,
definition,
`conversation Definition "${definition.kind}" is already registered`,
`conversationEvents.register(${JSON.stringify(definition.kind)})`,
)
}
/**
* Register the sole fallback used only when no ordinary Definition matches.
* @param definition - fallback Definition.
* @returns idempotent disposer.
*/
registerFallback(definition: ConversationNodeDefinition): () => void {
assertDefinitionTarget(definition)
const target = definition.target
if (target === undefined) throw new Error('conversation fallback Definition must declare a target')
if (this.fallback !== undefined) throw new Error('conversation fallback Definition is already registered')
const owner = this.ctx
const dispose = owner.effect(() => {
this.fallback = definition
this.refresh()
return () => {
if (this.fallback !== definition) return
this.fallback = undefined
this.refresh()
}
}, `conversationEvents.registerFallback(${JSON.stringify(definition.kind)})`)
return () => { void dispose() }
}
/**
* Return the current unmatched-event fallback.
* @returns installed fallback, when present.
*/
fallbackEntry(): ConversationNodeDefinition | undefined {View on GitHub (pinned to b150a551b8)
Solutions
- Keep exactly one fallback owner (the core conversation package); other features register keyed Definitions with unique kinds instead.
- If replacing the fallback is genuinely required, dispose the existing registration (or disable the owning plugin) first, then register — usually via restart.
- Remove fallback registration from test fixtures, or ensure the fallback-owning plugin is not composed in that test.
Example fix
// before — two packages both register a fallback
events.registerFallback(coreFallback)
events.registerFallback(customFallback) // throws: already registered
// after — one fallback owner; others contribute keyed Definitions
events.registerFallback(coreFallback)
events.register({ kind: 'my.card', ... }) Defensive patterns
Strategy: validation
Validate before calling
if (events.fallbackEntry() !== undefined) {
// the single fallback slot is owned; contribute a keyed Definition instead
events.register({ kind: 'my.kind', /* ... */ })
} else {
events.registerFallback(myFallback)
} Prevention
- Reserve registerFallback for the one owning package and document the ownership.
- Check fallbackEntry() before attempting to install a fallback.
- In tests, dispose the runtime between cases so the slot is free.
When it happens
Trigger: A second live call to conversationEvents.registerFallback() while a previous fallback registration is still installed — two packages both claiming the fallback role, or a re-mount whose earlier disposer has not run.
Common situations: A new plugin package tries to replace the built-in generic fallback by registering its own; test harnesses install a fallback fixture while the core conversation package (which owns the fallback) is also composed; HMR re-apply racing disposal.
Related errors
AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24).
Data as JSON: /api/errors/56e438dfef433780.
Report an issue: GitHub.