{"record":{"id":"f442edb5e35386e3","repo":"can1357/oh-my-pi","slug":"plugin-must-implement-onrecall","errorCode":null,"errorMessage":"Plugin must implement onRecall","messagePattern":"Plugin must implement onRecall","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/mnemopi/src/core/plugins.ts","lineNumber":40,"sourceCode":"\t\tthis.name =\n\t\t\t(ctor.prototype.name as string | undefined) ?? (ctor as unknown as { name?: string }).name ?? this.name;\n\t\tthis.version = (ctor.prototype.version as string | undefined) ?? this.version;\n\t\tthis.enabled = (ctor.prototype.enabled as boolean | undefined) ?? this.enabled;\n\t}\n\n\tinitialize(): void {\n\t\tthis.initialized = true;\n\t}\n\n\tshutdown(): void {\n\t\tthis.initialized = false;\n\t}\n\n\tonRemember(_memory: MemoryDict): void {\n\t\tthrow new TypeError(\"Plugin must implement onRemember\");\n\t}\n\tonRecall(_memory: MemoryDict): void {\n\t\tthrow new TypeError(\"Plugin must implement onRecall\");\n\t}\n\tonConsolidate(_summary: MemoryDict): void {\n\t\tthrow new TypeError(\"Plugin must implement onConsolidate\");\n\t}\n\tonInvalidate(_memoryId: string): void {\n\t\tthrow new TypeError(\"Plugin must implement onInvalidate\");\n\t}\n\ttoDict(): Record<string, unknown> {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tversion: this.version,\n\t\t\tenabled: this.enabled,\n\t\t\tinitialized: this.initialized,\n\t\t\tconfig: this.config,\n\t\t};\n\t}\n}\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/core/plugins.ts#L22-L58","documentation":"MnemopiPlugin's onRecall is a throwing stub: subclasses must override it because the plugin host invokes it on every memory recall. Hitting this TypeError means the registered plugin never implemented the recall hook.","triggerScenarios":"A plugin subclass registered with the host that does not override onRecall, triggered when a memory is recalled and the host iterates plugins calling plugin.onRecall(memory).","commonSituations":"Writing a plugin that only cares about onRemember/onConsolidate; a refactor renaming the method so the override no longer matches; loading a stale or minified plugin build without the override.","solutions":["Override onRecall(memory: MemoryDict): void in the plugin subclass","Provide a no-op override if recall events are irrelevant","Rebuild/redeploy the plugin so the override is actually present at runtime"],"exampleFix":"// before\nclass MyPlugin extends MnemopiPlugin { override onRemember(m: MemoryDict): void {} }\n// after\nclass MyPlugin extends MnemopiPlugin { override onRemember(m: MemoryDict): void {} override onRecall(m: MemoryDict): void { /* ... */ } }","handlingStrategy":"type-guard","validationCode":"function implementsOnRecall(p: MnemopiPlugin): boolean {\n  return p.onRecall !== MnemopiPlugin.prototype.onRecall;\n}\nif (!implementsOnRecall(plugin)) throw new Error(`plugin ${plugin.name} must override onRecall`);","typeGuard":"function overridesOnRecall<P extends MnemopiPlugin>(p: P): p is P & { onRecall(m: MemoryDict): void } {\n  return p.onRecall !== MnemopiPlugin.prototype.onRecall;\n}","tryCatchPattern":"try {\n  host.recall(id);\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"Plugin must implement onRecall\") {\n    logger.error(`plugin ${plugin.name} lacks onRecall; unregistering it`);\n    host.unregister(plugin);\n  } else throw err;\n}","preventionTips":["Scaffold new plugins from a template containing all hook stubs","Use `override` keyword and strict TypeScript checks to catch renamed methods","Run a plugin conformance test comparing each hook against MnemopiPlugin.prototype","Rebuild plugins after upgrades in case hook signatures changed"],"tags":["typescript","plugins","unimplemented-method"],"backgroundTag":"unimplemented-hook-method","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}