mermaid-js/mermaid · error

Invalid icon loader. Must have either "icons" or "loader" pr

Error message

Invalid icon loader. Must have either "icons" or "loader" property.

What it means

Thrown by registerIconPacks after the name check passes but the loader object has neither a `loader` function (async pack) nor an `icons` object (static pack). The registry stores one or the other keyed by name, so the object must carry exactly one of these properties. The preceding log.error('Invalid icon loader:', iconLoader) prints the offending object.

Source

Thrown at packages/mermaid/src/rendering-util/icons.ts:43

const iconsStore = new Map<string, IconifyJSON>();
const loaderStore = new Map<string, AsyncIconLoader['loader']>();

export const registerIconPacks = (iconLoaders: IconLoader[]) => {
  for (const iconLoader of iconLoaders) {
    if (!iconLoader.name) {
      throw new Error(
        'Invalid icon loader. Must have a "name" property with non-empty string value.'
      );
    }
    log.debug('Registering icon pack:', iconLoader.name);
    if ('loader' in iconLoader) {
      loaderStore.set(iconLoader.name, iconLoader.loader);
    } else if ('icons' in iconLoader) {
      iconsStore.set(iconLoader.name, iconLoader.icons);
    } else {
      log.error('Invalid icon loader:', iconLoader);
      throw new Error('Invalid icon loader. Must have either "icons" or "loader" property.');
    }
  }
};

const getRegisteredIconData = async (iconName: string, fallbackPrefix?: string) => {
  const data = stringToIcon(iconName, true, fallbackPrefix !== undefined);
  if (!data) {
    throw new Error(`Invalid icon name: ${iconName}`);
  }
  const prefix = data.prefix || fallbackPrefix;
  if (!prefix) {
    throw new Error(`Icon name must contain a prefix: ${iconName}`);
  }
  let icons = iconsStore.get(prefix);
  if (!icons) {
    const loader = loaderStore.get(prefix);
    if (!loader) {
      throw new Error(`Icon set not found: ${data.prefix}`);

View on GitHub (pinned to d93e9c88c0)

Solutions

  1. Provide exactly one of `loader: () => Promise<IconifyJSON>` (async/lazy) or `icons: IconifyJSON` (static/eager) on each loader object.
  2. For lazy loading use loader: () => import('@iconify-json/logos').then((m) => m.icons); for eager use import { icons } from '@iconify-json/logos' and pass { name, icons }.
  3. Check the console immediately before this error — log.error prints the full invalid object so you can see which key is missing/misspelled.
  4. Validate against the IconLoader union type so TypeScript flags objects missing the required discriminating property.

Example fix

// before
mermaid.registerIconPacks([{ name: 'logos' }]);
// after
mermaid.registerIconPacks([
  {
    name: 'logos',
    loader: () => import('@iconify-json/logos').then((m) => m.icons),
  },
]);
Defensive patterns

Strategy: type-guard

Validate before calling

function hasIconsOrLoader(x: any): boolean {
  return (typeof x.loader === 'function') || (x.icons && typeof x.icons === 'object');
}
for (const l of loaders) if (!hasIconsOrLoader(l)) throw new Error(`loader ${l.name} needs icons or loader`);

Type guard

function isAsyncIconLoader(x: any): x is { name: string; loader: () => Promise<any> } {
  return typeof x.name === 'string' && typeof x.loader === 'function';
}
function isSyncIconLoader(x: any): x is { name: string; icons: object } {
  return typeof x.name === 'string' && x.icons && typeof x.icons === 'object';
}

Try / catch

try { mermaid.registerIconPacks(loaders); } catch (e) { if (/icons or loader/.test(String(e))) console.error('each loader needs icons or loader:', loaders); throw e; }

Prevention

When it happens

Trigger: Passing { name: 'logos' } alone, { name: 'logos', src: '...' } (wrong key like `src`), or { name: 'logos', icon: {...} } (singular `icon` instead of `icons`). Any name-bearing object missing both `loader` and `icons` keys hits the final else branch.

Common situations: Mistyping the property (`Icon`/`Loader` capitalized, `iconData`, `data`), or upgrading from an older API where the shape differed. Also from partial spreads: { name: 'x', ...rest } where rest happened to omit both keys.

Related errors


AI-assisted analysis of mermaid-js/mermaid@d93e9c88c0 (2026-08-12). Data as JSON: /api/errors/ad79dee5b406530c. Report an issue: GitHub.