withastro/astro · error · Error

<script src="${src}"> references an asset in the "${public

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

Thrown by the Astro Vite plugin when a `<script src="/...">` in a `.astro` component points at a file inside the configured `publicDir` (default `public/`). Astro tries to bundle external script references, but files in `public/` are served as-is and must not be processed by the bundler. The fix is to mark the script `is:inline` so Astro leaves it untouched.

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 d081033d5f)

Solutions

  1. Add `is:inline` to the script tag: `<script is:inline src="/lib/analytics.js">`.
  2. Alternatively, move the file out of `public/` into `src/` and import it as a module so Astro can bundle it.
  3. If the script is a remote URL (https://), it is not affected — only absolute local paths starting with `/` are checked.

Example fix

<!-- before -->
<script src="/lib/analytics.js"></script>

<!-- after -->
<script is:inline src="/lib/analytics.js"></script>
Defensive patterns

Strategy: validation

Validate before calling

// In your .astro file, before referencing a public asset script:
const isPublicAsset = (src) => src.startsWith('/') && !src.startsWith('//');
// If true, always render with is:inline.

Prevention

When it happens

Trigger: Writing `<script src="/lib/analytics.js">` where `public/lib/analytics.js` exists. The plugin detects `src.startsWith('/') && !isBrowserPath(src)` and derives the publicDir name from `config.publicDir`. Any absolute `/...` script whose target lives under public triggers it.

Common situations: Adding a third-party script (analytics, widgets) by dropping the file into `public/` and referencing it from a layout. Migrating from plain HTML where `<script src>` worked without bundler awareness. Forgetting `is:inline` on scripts that should bypass Vite.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/442b9f72acee55a7. Report an issue: GitHub.