withastro/astro · error · Error
<script src=" "> references an asset in the " " directory…
Error message
<script src="${src}"> references an asset in the "${publicDir}" directory. Please add the "is:inline" directive to keep this asset from being bundled.
File: ${id} What it means
Astro bundles processed <script> tags (those without is:inline). If such a script's src is an absolute path (/...) that does not point inside the source root (src/ by default), Astro concludes it references a file in the public directory. public/ assets are copied verbatim and are outside the module graph, so Vite cannot bundle them — the build stops and asks you to keep the script as-is with the is:inline directive.
Solutions
- Add the is:inline directive: <script is:inline src="/js/analytics.js"></script>
- Or move the file out of public/ into the source tree (e.g. src/scripts/analytics.js) and reference it relatively so Vite bundles it
- Keep third-party CDN scripts (full https:// URLs) as-is; only root-relative /... paths that resolve into public/ hit this error
Example fix
<!-- before --> <script src="/js/analytics.js"></script> <!-- after --> <script is:inline src="/js/analytics.js"></script>
Defensive patterns
Strategy: validation
Validate before calling
// check whether a root-relative src points into public/ before writing the tag
import fs from 'node:fs';
import path from 'node:path';
const isPublicAsset = (src: string) =>
src.startsWith('/') && fs.existsSync(path.join('public', src));
// isPublicAsset('/js/analytics.js') === true -> needs is:inline Prevention
- Always add is:inline to scripts whose src points at files in public/
- Keep bundled scripts inside the source tree (src/) and reference them relatively
- Reserve public/ for assets copied verbatim — anything Vite should bundle belongs in src/
When it happens
Trigger: <script src="/js/analytics.js"></script> where the file lives at public/js/analytics.js; moving a bundled script into public/ without adding is:inline; root-relative paths to vendor files dropped into public/.
Common situations: Pasting <script src="..."> snippets from vendor docs into an .astro layout; migrating a static site whose scripts all lived in the public folder.
Related errors
- ServerOnlyModule
- [@astrojs/node] Could not find the server directory
- CannotDetermineWeightAndStyleFromFontFile
- CannotFetchFontFile
- Collection loader for
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/442b9f72acee55a7.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/vite-plugin-astro/index.ts:213
// SSR script only exists to make them appear in the module graph.
if (isAstroServerEnvironment(this.environment)) {
return {
code: `/* client script, empty in SSR: ${id} */`,
moduleType: 'ts',
};
}
const script = compileMetadata.scripts[query.index];
if (!script) {
throw new Error(`No script at index ${query.index}`);
}
if (script.type === 'external') {
const src = script.src!;
if (src.startsWith('/') && !isBrowserPath(src)) {
const publicDir =
config.publicDir.pathname.replace(/\/$/, '').split('/').pop() + '/';
throw new Error(
`\n\n<script src="${src}"> references an asset in the "${publicDir}" directory. Please add the "is:inline" directive to keep this asset from being bundled.\n\nFile: ${id}`,
);
}
}
const result: vite.Rolldown.SourceDescription = {
code: '',
moduleType: 'ts',
meta: {
vite: {
lang: 'ts',
},
},
};
switch (script.type) {
case 'inline': {
result.code = script.code ?? '';View on GitHub (pinned to 52e6c34790)