{"record":{"id":"64caff85c1ae861a","repo":"moeru-ai/airi","slug":"gameletkit-requires-a-host-binding-runtime","errorCode":null,"errorMessage":"gameletKit requires a host binding runtime.","messagePattern":"gameletKit requires a host binding runtime\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/plugin-sdk-tamagotchi/src/gamelet/index.ts","lineNumber":75,"sourceCode":"  id: 'kit.gamelet',\n  version: '1.0.0',\n  allowedExposePolicies: ['local-only', 'remote-observable'],\n  defaultExposePolicy: 'local-only',\n  createClient(runtime) {\n    const gameletRuntime = runtime as GameletKitRuntime\n    return {\n      iframe(input) {\n        return {\n          mount: 'iframe',\n          iframe: {\n            ...input,\n            sandbox: input.sandbox ?? 'allow-scripts allow-same-origin allow-forms allow-popups',\n          },\n        }\n      },\n      async mount(definition) {\n        if (!gameletRuntime.bindings) {\n          throw new Error('gameletKit requires a host binding runtime.')\n        }\n\n        return await gameletRuntime.bindings.bind({\n          moduleId: definition.bindingId ?? createGameletBindingId(runtime),\n          kitId: 'kit.gamelet',\n          kitModuleType: 'gamelet',\n          config: {\n            title: definition.title,\n            widget: definition.ui,\n            config: {\n              init: definition.init ?? {},\n            },\n          },\n        })\n      },\n      orchestration: gameletRuntime.gamelets,\n    }\n  },","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/plugin-sdk-tamagotchi/src/gamelet/index.ts#L57-L93","documentation":"In plugin-sdk-tamagotchi's gamelet kit (src/gamelet/index.ts), mount(definition) delegates the actual mounting of a gamelet iframe/widget to gameletRuntime.bindings.bind(...). bindings is a host-provided capability: only the real tamagotchi host process supplies it when it instantiates kit clients for an extension module. If the kit is used in a runtime whose host did not provide a binding runtime (bare SDK host, unit tests, a slim host), mount() refuses to proceed and throws this error rather than returning a handle that could never mount anything.","triggerScenarios":"Calling module.kits.use(gameletKit) and then gamelets.mount({...}) inside a unit test, a mock host built with createPluginHost but no binding runtime registered, or a host integration that deliberately omits UI binding capabilities. Also when code accidentally runs the SDK kit outside the tamagotchi application.","commonSituations":"Extension authors writing vitest suites against their gamelet module with a minimal host; a new host embedding the SDK that has not implemented bindings yet; calling mount during host shutdown when capabilities have been torn down.","solutions":["Run the code inside the real tamagotchi host, which provides gameletRuntime.bindings when creating kit clients.","If you are building/testing against a custom host, register a bindings implementation (bind({ moduleId, kitId, kitModuleType, config }) returning a mounted binding) in the runtime you pass to kit creation.","In tests, stub the kit or inject a fake bindings runtime instead of invoking the real mount path.","Feature-detect before mounting: skip/defer the mount when the host lacks bindings rather than crashing the extension."],"exampleFix":"// before\nconst gamelets = await module.kits.use(gameletKit)\nawait gamelets.mount(definition) // throws in tests / slim hosts\n\n// after\nconst gamelets = await module.kits.use(gameletKit)\nif (!gameletRuntimeHasBindings(gamelets)) {\n  console.warn('gamelet bindings unavailable; skipping mount')\n  return\n}\nawait gamelets.mount(definition)","handlingStrategy":"validation","validationCode":"const gamelets = await module.kits.use(gameletKit)\n// feature-detect host bindings before mounting\nconst canMount = 'mount' in gamelets && hostProvidesBindings()\nif (canMount) await gamelets.mount(definition)","typeGuard":"interface HostBindingsRuntime { bindings?: { bind(input: unknown): Promise<unknown> } }\nfunction hasGameletBindings(runtime: HostBindingsRuntime): boolean {\n  return typeof runtime.bindings?.bind === 'function'\n}","tryCatchPattern":"try {\n  return await gamelets.mount(definition)\n} catch (error) {\n  if (errorMessageFrom(error).includes('requires a host binding runtime')) {\n    return undefined // host without UI bindings: skip gamelet registration\n  }\n  throw error\n}","preventionTips":["Run gamelet mounting only inside the real tamagotchi host or a host that explicitly documents bindings support.","In tests, inject a fake bindings runtime instead of exercising the real mount path.","Feature-detect host capabilities once at module setup and record them, so every later call knows what is available."],"tags":["plugin-sdk","tamagotchi","gamelet","host-runtime","capabilities"],"backgroundTag":"host-runtime-capability-missing","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}