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

  1. Make the entry point default-export a function that receives the API: `export default (api: API) => { ... }`.
  2. 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.
  3. Check package.json 'main' points at the compiled file that actually contains the default export (e.g. dist/index.js, not src/index.ts).
  4. 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 }`.
  5. 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

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


AI-assisted analysis of homebridge/homebridge@edf5493034 (2026-08-30). Data as JSON: /api/errors/87bd5a5e90922c2f. Report an issue: GitHub.