moeru-ai/airi · error · Error
gameletKit requires a host binding runtime.
Error message
gameletKit requires a host binding runtime.
What it means
Thrown by gameletKit.mount() when the runtime it received has no `bindings` field. The kit is a client-side SDK; mount() delegates host-side module registration to runtime.bindings.bind(...). Without bindings, there is no host to register the gamelet against, so the call cannot proceed.
Source
Thrown at packages/plugin-sdk-tamagotchi/src/gamelet/index.ts:75
id: 'kit.gamelet',
version: '1.0.0',
allowedExposePolicies: ['local-only', 'remote-observable'],
defaultExposePolicy: 'local-only',
createClient(runtime) {
const gameletRuntime = runtime as GameletKitRuntime
return {
iframe(input) {
return {
mount: 'iframe',
iframe: {
...input,
sandbox: input.sandbox ?? 'allow-scripts allow-same-origin allow-forms allow-popups',
},
}
},
async mount(definition) {
if (!gameletRuntime.bindings) {
throw new Error('gameletKit requires a host binding runtime.')
}
return await gameletRuntime.bindings.bind({
moduleId: definition.bindingId ?? createGameletBindingId(runtime),
kitId: 'kit.gamelet',
kitModuleType: 'gamelet',
config: {
title: definition.title,
widget: definition.ui,
config: {
init: definition.init ?? {},
},
},
})
},
orchestration: gameletRuntime.gamelets,
}
},View on GitHub (pinned to 27111382b4)
Solutions
- Ensure the runtime passed to gameletKit.createClient includes a `bindings` object whose bind() registers the module host-side.
- If you only need to render UI without host registration, use kit.iframe(...) instead of kit.mount(...).
- In tests, provide a stub runtime: { moduleId: 'test', sessionId: 's', bindings: { bind: async () => {} } }.
- Verify the host (stage-tamagotchi) wires bindings into the runtime before instantiating the kit client.
Example fix
// before
const kit = gameletKit.createClient({ moduleId: 'demo', sessionId: 's1' })
await kit.mount({ title: 't', ui: widget }) // throws: no bindings
// after
const kit = gameletKit.createClient({
moduleId: 'demo', sessionId: 's1',
bindings: { bind: async (input) => hostRegister(input) },
})
await kit.mount({ title: 't', ui: widget }) Defensive patterns
Strategy: type-guard
Validate before calling
function hasBindings(runtime: unknown): runtime is { bindings: { bind: (i: unknown) => unknown } } {
return !!runtime && typeof runtime === 'object' && !!(runtime as any).bindings && typeof (runtime as any).bindings.bind === 'function'
}
if (!hasBindings(runtime)) {
throw new Error('Runtime has no host bindings; use kit.iframe() or attach a host binding runtime.')
}
await kit.mount({ title, ui }) Type guard
import type { GameletKitRuntime } from '@proj-airi/plugin-sdk-tamagotchi/gamelet'
function hasHostBindings(runtime: unknown): runtime is GameletKitRuntime {
return !!runtime && typeof runtime === 'object' && typeof (runtime as any).bindings?.bind === 'function'
} Try / catch
try {
await kit.mount({ title, ui })
} catch (error) {
if (error instanceof Error && error.message.includes('host binding runtime')) {
// fall back to iframe rendering, or attach bindings and retry
kit.iframe({ src })
} else throw error
} Prevention
- Attach a bindings object to the runtime whenever the kit runs in a host context.
- Use kit.iframe() when no host binding runtime is available.
- In tests, stub runtime.bindings.bind to satisfy the guard.
When it happens
Trigger: Calling kit.mount(...) on a gameletKit client whose runtime was constructed for a renderer-only/plugin-only context (no host binding runtime attached); using defineKit's gameletKit in a non-host environment; the host failed to inject bindings when constructing the runtime.
Common situations: Plugin author testing the kit outside the tamagotchi host (e.g. unit test with a minimal runtime stub); host integration that forgot to attach bindings to the runtime; a plugin loaded in a context that only supports iframe() but not mount(); version skew between plugin-sdk and the host providing the runtime.
Related errors
- gameletKit requires a host gamelet orchestration runtime.
- Gamelet `${bindingId}` is not open.
- Unknown tool: ${tool}
- skip() cannot be mixed with other tool calls in the same scr
- Action limit exceeded: max ${this.maxActionsPerTurn} actions
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/64caff85c1ae861a.
Report an issue: GitHub.