vitejs/vite · error · Error

" } is not supported for assets in files

Error message

{ runtime: "${result.runtime}" } is not supported for assets in ${hostType} files: ${filename}

What it means

Thrown by plugin-legacy's internal toOutputFilePathInHtml helper when Vite's experimental.renderBuiltUrl hook returns an object containing a non-empty `runtime` string. Runtime-resolved URLs are deferred to the browser, but plugin-legacy generates static HTML/CSS/JS host tags whose asset URLs must be baked in at build time, so a runtime value is impossible to honor here. The message echoes the offending runtime value, the host file type, and the asset filename so you can locate the mismatch.

Solutions

  1. In your `renderBuiltUrl` callback, return a plain string (or `{ relative: boolean }`) instead of `{ runtime: ... }` for assets referenced from HTML hosts.
  2. Branch the `renderBuiltUrl` logic on `hostType`/`type` so HTML-served assets get a static path while module assets can still use runtime resolution.
  3. Remove the `experimental.renderBuiltUrl` option if you do not need runtime asset URLs.
  4. If you must keep runtime URLs, disable plugin-legacy for the affected build (set `renderLegacyChunks: false` / drop the plugin) so the legacy HTML path is not exercised.

Example fix

// before
experimental: {
  renderBuiltUrl(filename, { hostType }) {
    return { runtime: `window.__assets[${JSON.stringify(filename)}]` }
  },
},
// after
experimental: {
  renderBuiltUrl(filename, { hostType, type }) {
    if (hostType === 'html') return `/static/${filename}` // static for HTML hosts
    return { runtime: `window.__assets[${JSON.stringify(filename)}]` }
  },
},
Defensive patterns

Strategy: validation

Validate before calling

// Validate renderBuiltUrl return shape before enabling plugin-legacy
import type { ResolvedConfig } from 'vite'
function assertLegacySafeRenderBuiltUrl(cfg: ResolvedConfig) {
  const fn = cfg.experimental?.renderBuiltUrl
  if (!fn) return
  // simulate with an html-host asset to detect a runtime return
  const probe = fn('assets/x.js', { hostId: 'index.html', hostType: 'html', type: 'asset', ssr: false })
  if (probe && typeof probe === 'object' && 'runtime' in probe && probe.runtime) {
    throw new Error('renderBuiltUrl returns { runtime } for HTML assets - incompatible with plugin-legacy')
  }
}

Type guard

function isStaticBuiltUrlResult(r: unknown): r is string | { relative?: boolean } {
  if (r == null) return true
  if (typeof r === 'string') return true
  if (typeof r === 'object' && r !== null && !('runtime' in r && (r as any).runtime)) return true
  return false
}

Try / catch

try {
  await vite.build(config)
} catch (e) {
  if (e instanceof Error && /is not supported for assets in .* files/.test(e.message)) {
    // fix renderBuiltUrl to return a static path for HTML hostType, then rebuild
  } else throw e
}

Prevention

When it happens

Trigger: Configuring `experimental.renderBuiltUrl` in vite.config to return `{ runtime: '...' }` (or `{ runtime: someVar }`) for an asset that ends up referenced from an HTML entry processed by @vitejs/plugin-legacy, then running a production build (`vite build`). The throw fires inside the legacy plugin's `transformIndexHtml`/asset path resolution because it calls the duplicated `toOutputFilePathInHtml`.

Common situations: Copying a `renderBuiltUrl` snippet from Vite docs (designed for CDN/runtime base URLs) into a project that also uses @vitejs/plugin-legacy. Migrating an app to a runtime-determined asset base (e.g. loading assets from a dynamic CDN) without realizing legacy HTML injection needs a static URL.

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/b079207d28ff8f6a. Report an issue: GitHub.

Appendix: source

Thrown at packages/plugin-legacy/src/index.ts:81

  filename: string,
  type: 'asset' | 'public',
  hostId: string,
  hostType: 'js' | 'css' | 'html',
  config: ResolvedConfig,
  toRelative: (filename: string, importer: string) => string,
): string {
  const { renderBuiltUrl } = config.experimental
  let relative = config.base === '' || config.base === './'
  if (renderBuiltUrl) {
    const result = renderBuiltUrl(filename, {
      hostId,
      hostType,
      type,
      ssr: !!config.build.ssr,
    })
    if (typeof result === 'object') {
      if (result.runtime) {
        throw new Error(
          `{ runtime: "${result.runtime}" } is not supported for assets in ${hostType} files: ${filename}`,
        )
      }
      if (typeof result.relative === 'boolean') {
        relative = result.relative
      }
    } else if (result) {
      return result
    }
  }
  if (relative && !config.build.ssr) {
    return toRelative(filename, hostId)
  } else {
    // @ts-expect-error `decodedBase` is internal
    return joinUrlSegments(config.decodedBase, filename)
  }
}
function getBaseInHTML(urlRelativePath: string, config: ResolvedConfig) {

View on GitHub (pinned to b4d66fee14)