{"record":{"id":"c1286c86c1b1002e","repo":"eyaltoledano/claude-task-master","slug":"provider-must-implement-baseaiprovider-interface","errorCode":null,"errorMessage":"Provider must implement BaseAIProvider interface","messagePattern":"Provider must implement BaseAIProvider interface","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/provider-registry/index.js","lineNumber":70,"sourceCode":"\t * @param {object} options - Additional options for registration\n\t * @returns {ProviderRegistry} The singleton instance for chaining\n\t */\n\tregisterProvider(providerName, provider, options = {}) {\n\t\tif (!providerName || typeof providerName !== 'string') {\n\t\t\tthrow new Error('Provider name must be a non-empty string');\n\t\t}\n\n\t\tif (!provider) {\n\t\t\tthrow new Error('Provider instance is required');\n\t\t}\n\n\t\t// Validate that provider implements the required interface\n\t\tif (\n\t\t\ttypeof provider.generateText !== 'function' ||\n\t\t\ttypeof provider.streamText !== 'function' ||\n\t\t\ttypeof provider.generateObject !== 'function'\n\t\t) {\n\t\t\tthrow new Error('Provider must implement BaseAIProvider interface');\n\t\t}\n\n\t\t// Add provider to the registry\n\t\tthis._providers.set(providerName, {\n\t\t\tinstance: provider,\n\t\t\toptions,\n\t\t\tregisteredAt: new Date()\n\t\t});\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Check if a provider exists in the registry\n\t * @param {string} providerName - The name of the provider\n\t * @returns {boolean} True if the provider exists\n\t */\n\thasProvider(providerName) {","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/src/provider-registry/index.js#L52-L88","documentation":"registerProvider() enforces the BaseAIProvider contract: any registered provider must implement generateText, streamText, and generateObject as functions. This duck-typing check runs at registration time so that lookup failures surface early instead of when a consumer first calls the provider. Passing a plain object, a partial implementation, or a misnamed method triggers this error.","triggerScenarios":"registerProvider('custom', { generateText: fn }) missing streamText/generateObject; registering a class reference instead of an instance (methods not bound/instance methods not on the object); a v2 provider with renamed methods registered into a registry expecting the BaseAIProvider interface.","commonSituations":"Writing a custom provider from docs and only implementing one or two methods; refactoring method names (e.g. generate → generateText) without updating all interface methods; mocking providers in tests with incomplete stubs; registering objects that proxy methods via getters, which typeof checks at registration may miss if lazily defined.","solutions":["Extend BaseAIProvider (or implement all three methods: generateText, streamText, generateObject) before registering.","console.log(typeof p.generateText, typeof p.streamText, typeof p.generateObject) to see which method is missing.","If you meant to pass a class, instantiate it: new MyProvider() so prototype methods exist on the instance.","Update your stub/mock to implement the full interface when testing.","Check for version drift between your provider package and the registry's expected interface."],"exampleFix":"// before\nregistry.registerProvider('custom', {\n  generateText: async (p) => 'hi'\n});\n// after\nclass CustomProvider extends BaseAIProvider {\n  async generateText(prompt) { return 'hi'; }\n  async streamText(prompt, cb) { cb('hi'); }\n  async generateObject(prompt, schema) { return {}; }\n}\nregistry.registerProvider('custom', new CustomProvider());","handlingStrategy":"validation","validationCode":"function assertImplementsBaseAIProvider(p) {\n  const required = ['generateText', 'streamText', 'generateObject'];\n  const missing = required.filter((m) => typeof p?.[m] !== 'function');\n  if (missing.length) {\n    throw new TypeError(`Provider missing BaseAIProvider methods: ${missing.join(', ')}`);\n  }\n}\nassertImplementsBaseAIProvider(myProvider);\nregistry.registerProvider('custom', myProvider);","typeGuard":"const implementsBaseAIProvider = (p) =>\n  p != null &&\n  typeof p.generateText === 'function' &&\n  typeof p.streamText === 'function' &&\n  typeof p.generateObject === 'function';","tryCatchPattern":"try {\n  registry.registerProvider('custom', provider);\n} catch (err) {\n  if (err.message.includes('BaseAIProvider interface')) {\n    console.error('Provider', provider?.constructor?.name, 'must implement generateText, streamText, generateObject');\n    provider = new BaseAIProviderAdapter(provider); // wrap/adapt\n    registry.registerProvider('custom', provider);\n    return;\n  }\n  throw err;\n}","preventionTips":["Always extend BaseAIProvider for custom providers.","Implement all three interface methods even if some are stubs that throw 'not supported'.","Instantiate classes (new X()) rather than registering the class itself.","Pin provider-package versions and review changelogs for interface method renames.","Write a shared test asserting your provider passes the interface check."],"tags":["validation","provider-registry","interface-contract","duck-typing"],"backgroundTag":"interface-not-implemented","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}