angular/components · error

Cannot fetch icon from URL "${safeUrl}".

Error message

Cannot fetch icon from URL "${safeUrl}".

What it means

_fetchIcon builds the request from iconConfig.url; if that sanitized URL is null (e.g. the config was created without a URL), it throws a template error naming the attempted URL. This guards against config objects that only carry a literal (or nothing) being routed through the URL-fetch path.

Source

Thrown at src/material/icon/icon-registry.ts:641

    return svg;
  }

  /**
   * Returns an Observable which produces the string contents of the given icon. Results may be
   * cached, so future calls with the same URL may not cause another HTTP request.
   */
  private _fetchIcon(iconConfig: SvgIconConfig): Observable<TrustedHTML> {
    const {url: safeUrl, options} = iconConfig;
    const withCredentials = options?.withCredentials ?? false;

    if (!this._httpClient) {
      throw getMatIconNoHttpProviderError();
    }

    // TODO: add an ngDevMode check
    if (safeUrl == null) {
      throw Error(`Cannot fetch icon from URL "${safeUrl}".`);
    }

    const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);

    // TODO: add an ngDevMode check
    if (!url) {
      throw getMatIconFailedToSanitizeUrlError(safeUrl);
    }

    // Store in-progress fetches to avoid sending a duplicate request for a URL when there is
    // already a request in progress for that URL. It's necessary to call share() on the
    // Observable returned by http.get() so that multiple subscribers don't cause multiple XHRs.
    const inProgressFetch = this._inProgressUrlFetches.get(url);

    if (inProgressFetch) {
      return inProgressFetch;
    }

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Ensure a valid SafeResourceUrl is passed: sanitizer.bypassSecurityTrustResourceUrl('assets/icons.svg').
  2. Check that the name/namespace passed to addSvgIcon matches what is used in [svgIcon] so the right config is fetched.
  3. Log the registry config before fetching; replace null entries with real URLs or literal registrations.

Example fix

// before
registry.addSvgIcon('gear', undefined as any);
// after
registry.addSvgIcon('gear', this.sanitizer.bypassSecurityTrustResourceUrl('assets/gear.svg'));
Defensive patterns

Strategy: validation

Validate before calling

const url = sanitizer.bypassSecurityTrustResourceUrl('assets/icons.svg');
if (!url || !('changingThisBreaksApplicationSecurity' in url)) {
  throw new Error('Icon URL must be a non-null SafeResourceUrl');
}
registry.addSvgIcon('set', url);

Type guard

function isSafeResourceUrl(v: unknown): v is SafeResourceUrl {
  return !!v && typeof v === 'object' &&
    'changingThisBreaksApplicationSecurity' in (v as object);
}

Try / catch

try {
  await firstValueFrom(registry.getNamedSvgIcon(name));
} catch (e) {
  if ((e as Error).message.startsWith('Cannot fetch icon from URL')) {
    console.error(`Icon config for "${name}" has no URL; register a literal instead`);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Internal path where a SvgIconConfig has url == null when the fetch logic runs — practically caused by calling an API that produced a null/bypassed URL value, e.g. passing undefined/null instead of a SafeResourceUrl into addSvgIcon.

Common situations: Passing a plain string or undefined instead of sanitizer.bypassSecurityTrustResourceUrl(...) and having it coerced to null; config created by addSvgIconLiteral but fetched via the URL path; refactor errors after version upgrades.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/29f0f1b6a11d4e67. Report an issue: GitHub.