homebridge/homebridge · error
Plugin ${this.pluginPath} does not export an initializer fun
Error message
Plugin ${this.pluginPath} does not export an initializer function from ${this.main}. Found default export of type '${typeof pluginModules}'${defaultKeysStr}${exportedKeysStr}. The plugin must default-export a function that takes the homebridge api object. What it means
Homebridge expects every plugin's module entry point (package.json 'main') to default-export an initializer function `(api: HomebridgeAPI) => void`. If the default export is not a function (object, class instance, undefined, etc.), Plugin.load() throws with the detected export type plus its keys to help identify the mistake.
Source
Thrown at src/plugin.ts:237
const pluginModules = pluginModule.default
if (typeof pluginModules === 'function') {
this.pluginInitializer = pluginModules
} else if (pluginModules && typeof pluginModules.default === 'function') {
this.pluginInitializer = pluginModules.default
} else {
// Be specific about what we found instead of just "doesn't export an
// initializer". Helps plugin authors and users diagnose ESM/CJS
// shape mismatches at a glance.
const exportedKeys = pluginModule && typeof pluginModule === 'object'
? Object.keys(pluginModule).filter(k => k !== 'default').slice(0, 10)
: []
const defaultKeys = pluginModules && typeof pluginModules === 'object'
? Object.keys(pluginModules).slice(0, 10)
: []
const defaultKeysStr = defaultKeys.length > 0 ? ` with keys [${defaultKeys.join(', ')}]` : ''
const exportedKeysStr = exportedKeys.length > 0 ? `; named exports: [${exportedKeys.join(', ')}]` : ''
throw new Error(
`Plugin ${this.pluginPath} does not export an initializer function from ${this.main}. Found default export of type '${typeof pluginModules}'${defaultKeysStr}${exportedKeysStr}. The plugin must default-export a function that takes the homebridge api object.`,
)
}
}
public initialize(api: API): void | Promise<void> {
if (!this.pluginInitializer) {
throw new Error('Tried to initialize a plugin which hasn\'t been loaded yet!')
}
return this.pluginInitializer(api)
}
}
View on GitHub (pinned to edf5493034)
Solutions
- Make the entry point default-export a function that receives the API: `export default (api: API) => { ... }`.
- If the plugin uses a class, export a factory: `export default (api) => new MyPlugin(api)` or keep `export default MyPlugin` only if it matches the expected initializer shape.
- Check package.json 'main' points at the compiled file that actually contains the default export (e.g. dist/index.js, not src/index.ts).
- Inspect the named-exports list in the message; if your initializer is a named export like `initialize`, re-export it as default: `export { initialize as default }`.
- Rebuild the plugin and confirm the bundler emits a true default export (`node -e "import('...').then(m=>console.log(typeof m.default))"`).
Example fix
// before: index.ts
class MyPlatform { constructor(log, config) {} }
export { MyPlatform }
// after
class MyPlatform { constructor(log, config) {} }
export default (api: API) => {
api.registerPlatform('MyPlatform', MyPlatform)
} Defensive patterns
Strategy: validation
Validate before calling
const mod = await import(pluginMain)
if (typeof mod.default !== 'function') {
throw new Error(`Plugin entry must default-export a function; got ${typeof mod.default}`)
} Type guard
function isPluginInitializer(m: unknown): m is (api: unknown) => void | Promise<void> {
return typeof m === 'function'
} Try / catch
try {
await plugin.initialize(api)
} catch (e) {
log.error(`Plugin failed to load: ${(e as Error).message}`)
} Prevention
- Scaffold plugins from homebridge-plugin-template which has the correct export shape.
- Set package.json 'main' to the compiled dist file, never a .ts source.
- Smoke-test with `node -e "import('./dist/index.js').then(m=>console.log(typeof m.default))"`.
- Avoid `export default { ... }` namespace objects; default-export a function.
When it happens
Trigger: After dynamic import of this.main, `typeof pluginModules.default !== 'function'`. Includes undefined default export, an exported object literal, a class (non-callable-as-initializer shape), or everything exported only via named exports.
Common situations: TypeScript/ESM plugins written with `export default { ... }` or only `export class MyPlugin`; CommonJS plugins assigning `module.exports = { initialize }` without a default; bundlers (esbuild/rollup) misconfigured with wrong output format so the default export lands in a namespace object; converting a plugin from CommonJS to ESM and dropping `export default`.
Related errors
- Plugin ${this.pluginPath} does not contain the 'homebridge'
- Tried to initialize a plugin which hasn't been loaded yet!
- Cannot extract plugin name from invalid identifier: '${name}
- Cannot extract plugin scope from invalid identifier: '${name
- Unexpected accessory registration. Plugin ${pluginIdentifier
AI-assisted analysis of homebridge/homebridge@edf5493034 (2026-08-30).
Data as JSON: /api/errors/87bd5a5e90922c2f.
Report an issue: GitHub.