vitejs/vite · error · Error
{ runtime: "${result.runtime}" } is not supported for assets
Error message
{ runtime: "${result.runtime}" } is not supported for assets in ${hostType} files: ${filename} What it means
Thrown by @vitejs/plugin-legacy's internal toOutputFilePathInHtml when the experimental.renderBuiltUrl hook returns an object containing a `runtime` property for an asset referenced inside an HTML file. While `{ runtime }` is a valid return for JS/CSS hosts (Vite core emits it as a runtime-evaluated expression), HTML files cannot execute arbitrary runtime expressions to resolve asset URLs, so plugin-legacy explicitly rejects it. This is a deliberate constraint of the legacy plugin's duplicated HTML path resolver.
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 89620f09af)
Solutions
- In your renderBuiltUrl callback, check the hostType argument and only return { runtime } when hostType is 'js' or 'css'; return a plain string or { relative } for 'html'.
- If you only need runtime resolution for JS chunks, guard the callback with `if (type.hostType === 'html') return undefined` to fall back to default HTML asset path resolution.
- If runtime resolution in HTML is genuinely needed, inline a <script> that sets window-level base before asset tags rather than using renderBuiltUrl.
Example fix
// before
renderBuiltUrl(filename) {
return { runtime: `window.__CDN__ + ${JSON.stringify(filename)}` }
}
// after
renderBuiltUrl(filename, { hostType }) {
if (hostType === 'js' || hostType === 'css') {
return { runtime: `window.__CDN__ + ${JSON.stringify(filename)}` }
}
return undefined // let plugin-legacy handle html assets normally
} Defensive patterns
Strategy: validation
Validate before calling
// Validate renderBuiltUrl return before it reaches plugin-legacy
function safeRenderBuiltUrl(filename, { hostType }) {
const result = myRenderBuiltUrl(filename, { hostType })
if (
typeof result === 'object' &&
result?.runtime &&
hostType === 'html'
) {
console.warn(`renderBuiltUrl: runtime not supported for html assets, falling back for ${filename}`)
return undefined
}
return result
} Type guard
function isHtmlSafeRenderResult(
result: unknown,
hostType: string
): result is string | { relative?: boolean } | undefined {
if (result == null || typeof result === 'string') return true
if (typeof result === 'object') {
if (hostType === 'html' && 'runtime' in result && result.runtime) return false
return true
}
return false
} Try / catch
// Wrap the vite.config export to validate at config resolution time
try {
const cfg = defineConfig({
experimental: {
renderBuiltUrl(filename, ctx) {
const result = customUrlFn(filename, ctx)
if (ctx.hostType === 'html' && typeof result === 'object' && result?.runtime) {
throw new Error(`runtime not supported for html asset: ${filename}`)
}
return result
}
}
})
} catch (e) {
console.error('renderBuiltUrl config error:', e.message)
} Prevention
- Always branch on hostType in renderBuiltUrl and only return { runtime } for 'js'/'css'.
- Write a unit test that calls renderBuiltUrl with every hostType value to catch regressions.
- Review the RenderBuiltAssetUrl type signature — runtime is allowed by the type but not by all hosts.
When it happens
Trigger: Configuring experimental.renderBuiltUrl in vite.config to return { runtime: '...' } unconditionally for every asset, regardless of hostType. Specifically when the hostType is 'html' (i.e., assets referenced in index.html) and the callback does not branch on the hostType/type arguments passed to it.
Common situations: A developer adds experimental.renderBuiltUrl to dynamically rewrite CDN or base URLs at runtime. They return { runtime: 'window.__ASSET_BASE__ + "..."' } for all files without checking the hostType. This works for JS chunks (handled by Vite core's toOutputFilePathInJS at build.ts:1667) but breaks when plugin-legacy processes the same assets inside HTML templates.
Related errors
- `renderLegacyChunks` and `renderModernChunks` cannot be both
- @vitejs/plugin-legacy does not support library mode.
- Internal @vitejs/plugin-legacy error: discovered polyfills s
- Internal @vitejs/plugin-legacy error: discovered polyfills f
- No corresponding modern polyfill chunk found for ${htmlFilen
AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03).
Data as JSON: /data/errors/b079207d28ff8f6a.json.
Report an issue: GitHub.