Skip to content

HACS downloads panel shows 'icon not available' for custom integrations shipping inline brand icons (HA 2026.3+ brands proxy) #5223

@iskael

Description

@iskael

Web browser

Chromium-based (Chrome/Edge) and Firefox — any recent browser

Web browser version

Reproduces on all current browsers; the bug is a static code gap in the HACS frontend bundle, not a browser-specific rendering issue

System Health details

  • Home Assistant: 2026.4.2
  • HACS integration: 2.0.5 (latest release, confirmed up to date via `update.hacs_update`)
  • HACS frontend bundle: latest published release is `20250128065759` (Jan 28, 2025)
  • Custom integration reproducing the issue: `iskael/bosch-dishwasher`, which ships inline brand icons under `custom_components/bosch_dishwasher/brand/` per the new API

Checklist

  • I'm running the newest version of HACS
  • I have filled out the issue template to the best of my ability
  • I have read https://hacs.xyz/docs/help/issues/
  • This issue is related to the frontend of HACS
  • This issue only contains 1 issue
  • This is a bug and not a feature request
  • This issue is not a duplicate

Describe the issue

Starting with Home Assistant 2026.3, custom integrations can ship their own brand icons directly inside the integration (`custom_components//brand/icon.png`), and HA serves them through a new authenticated proxy at `/api/brands/integration//`. See the official announcement: https://developers.home-assistant.io/blog/2026/02/24/brands-proxy-api. The `home-assistant/brands` repository now auto-closes any PR for `custom_integrations/*` and tells contributors to use the inline mechanism instead.

The problem is that the HACS frontend still hits the public CDN for custom-integration icons, so inline brands are invisible in the HACS downloads panel — the row shows the "icon not available" placeholder even though the icon is correctly served by the local proxy.

Root cause (verified against the source):

`src/dashboards/hacs-dashboard.ts` imports `brandsUrl` from the pinned `homeassistant-frontend` submodule and calls it like this:

```ts
<img
src=${brandsUrl({
domain: repository.domain || "invalid",
type: "icon",
useFallback: true,
darkOptimized: this.hass.themes?.darkMode,
})}
/>
```

`useFallback` is a field from the old `BrandsOptions` interface that no longer exists in `home-assistant/frontend` `dev`. The current implementation was rewritten for the brands proxy:

```ts
// home-assistant/frontend/src/util/brands-url.ts (current)
export const brandsUrl = (options: BrandsOptions, hassUrl?: string): string => {
hassUrl = hassUrl ?? location.origin;
const base = `/api/brands/integration/${options.domain}/${
options.darkOptimized ? "dark_" : ""
}${options.type}.png`;
const url = new URL(base, hassUrl);
if (_brandsAccessToken) {
url.searchParams.set("token", _brandsAccessToken);
}
return url.toString();
};
```

HACS's pinned submodule is still on the old version that returns `https://brands.home-assistant.io/_/{domain}/icon.png\` directly to the browser. That CDN path doesn't serve inline-shipped custom-integration icons, so custom integrations on HA 2026.3+ that use the new mechanism get the placeholder.

Also worth noting: the maintainers of `home-assistant/brands` already redirect contributors here. Closed PR home-assistant/brands#10149 from this thread was auto-closed with the exact message "we no longer accept brand icons for custom integrations in this repository".

Reproduction steps

  1. Run Home Assistant 2026.3.0 or newer (reproduced on 2026.4.2).
  2. Install any custom integration that ships brand icons inline under `custom_components//brand/icon.png`. A minimal example is `iskael/bosch-dishwasher` (v0.1.4 and later).
  3. Confirm that HA core renders the icon in Settings → Devices & services (it does — served via `/api/brands/integration//icon.png` with an auth token).
  4. Open HACS → Downloads and look at the row for that integration.
  5. The row shows the generic "icon not available" placeholder instead of the inline icon.

Direct verification against the HA instance:

```

200, image/png, matches the bytes of brand/icon.png

curl -H "Authorization: Bearer " \
http://HA/api/brands/integration/bosch_dishwasher/icon.png

→ 200, image/png, 2515 bytes

without the token the proxy returns 403

curl http://HA/api/brands/integration/bosch_dishwasher/icon.png

→ 403

```

Screenshots

HACS downloads panel showing "icon not available"

The same integration renders its icon correctly in HA core's integration list and on the config-flow dialog — the gap is specific to the HACS panel.

Javascript logs from your browser console

No runtime errors. The bug is a static code gap: the bundled `brandsUrl` returns the old CDN URL (`https://brands.home-assistant.io/_/bosch_dishwasher/icon.png\`), the browser requests it, the CDN responds 404, and HACS's `` falls back to the placeholder. No exceptions are thrown.

Debug logs

Not applicable. This is not a runtime error in the integration; it is reproducible by reading the source of `src/dashboards/hacs-dashboard.ts` and the pinned `homeassistant-frontend` submodule. Enabling HACS debug logging produces no additional information because the icon resolution happens entirely on the frontend.

Diagnostics dump

Not applicable for the same reason — the HACS integration diagnostics do not cover frontend bundle paths. Happy to provide one if the maintainers believe it would add information.

Requested change

  1. Bump the pinned `homeassistant-frontend` submodule past the brands-proxy rewrite (so `brandsUrl` points at `/api/brands/integration/...` and uses the access token flow).
  2. In `src/dashboards/hacs-dashboard.ts`, drop `useFallback: true` (no longer a valid `BrandsOptions` field) and call `fetchAndScheduleBrandsAccessToken(this.hass)` during panel initialization so `` URLs carry a valid `?token=...` query string.
  3. Cut a new HACS frontend release — the last published one is `20250128065759`, which predates the HA 2026.3 brands-proxy feature.

With those three changes the HACS panel will pick up inline brand icons automatically, no PR to `home-assistant/brands` required (which is the officially supported path now).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions