{"id":"a68f57a6541a8d91","repo":"vitejs/vite","slug":"cannot-import-non-asset-file-specifier-which-is","errorCode":null,"errorMessage":"Cannot import non-asset file ${specifier} which is inside /public. JS/CSS files inside /public are copied as-is on build and can only be referenced via <script src> or <link href> in html. If you want to get the URL of that file, use ${injectQuery(specifier, 'url')} instead.","messagePattern":"Cannot import non-asset file (.+?) which is inside /public\\. JS/CSS files inside /public are copied as-is on build and can only be referenced via <script src> or <link href> in html\\. If you want to get the URL of that file, use (.+?) instead\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vite/src/node/plugins/importAnalysis.ts","lineNumber":578,"sourceCode":"              if (isBuiltin(environment.config.resolve.builtins, specifier)) {\n                return\n              }\n            }\n            // skip client\n            if (specifier === clientPublicPath) {\n              return\n            }\n\n            // warn imports to non-asset /public files\n            if (\n              specifier[0] === '/' &&\n              !(\n                config.assetsInclude(cleanUrl(specifier)) ||\n                urlRE.test(specifier)\n              ) &&\n              checkPublicFile(specifier, config)\n            ) {\n              throw new Error(\n                `Cannot import non-asset file ${specifier} which is inside /public. ` +\n                  `JS/CSS files inside /public are copied as-is on build and ` +\n                  `can only be referenced via <script src> or <link href> in html. ` +\n                  `If you want to get the URL of that file, use ${injectQuery(\n                    specifier,\n                    'url',\n                  )} instead.`,\n              )\n            }\n\n            // normalize\n            let [url, resolvedId] = await normalizeUrl(specifier, start)\n            resolvedId = resolvedId || url\n\n            // record as safe modules\n            // safeModulesPath should not include the base prefix.\n            // See https://github.com/vitejs/vite/issues/9438#issuecomment-1465270409\n            config.safeModulePaths.add(fsPathFromUrl(stripBase(url, base)))","sourceCodeStart":560,"sourceCodeEnd":596,"githubUrl":"https://github.com/vitejs/vite/blob/89620f09afcfef6b35e7bb8660132ab5b4d0cd3b/packages/vite/src/node/plugins/importAnalysis.ts#L560-L596","documentation":"Thrown by import analysis when a JS/CSS import specifier resolves to a file inside the `/public` directory. Files in `public` are served/copied verbatim and are not processed as modules, so importing them as JS/CSS is disallowed; the message suggests using `?url` to get the file's URL instead.","triggerScenarios":"A specifier starting with `/` that is not matched by `assetsInclude` or `?url`, but IS found by `checkPublicFile` (i.e. exists in `publicDir`) — e.g. `import data from '/data.js'` where `public/data.js` exists.","commonSituations":"Moving a JS/CSS/JSON file into `public/` (intending it as a static asset) but still importing it from source; importing a config/script that lives in `public`; misunderstanding that `public` files are for HTML `<script src>`/`<link href>` references, not ESM imports.","solutions":["Move the file out of `public` into `src` (or elsewhere) and import it normally.","If you only need its URL, append `?url`: `import dataUrl from '/data.js?url'`.","Reference public files via HTML `<script src=\"/data.js\">` / `<link href=\"/style.css\">` instead of importing.","If the file is genuinely static data, fetch it at runtime via `fetch('/data.json')`."],"exampleFix":"// before — file at public/data.js\nimport data from '/data.js';\n// after — move to src/data.js\nimport data from './data.js';\n// or, if you just need the URL\nimport dataUrl from '/data.js?url';","handlingStrategy":"validation","validationCode":"import { existsSync } from 'node:fs';\nimport path from 'node:path';\n\nfunction assertNotPublicImport(specifier, publicDir) {\n  if (specifier.startsWith('/') && existsSync(path.join(publicDir, specifier))) {\n    throw new Error(`Do not import public file as a module: ${specifier}. Use ?url or move it to src.`);\n  }\n}\n// assertNotPublicImport(specifier, config.publicDir);","typeGuard":"function isPublicImport(specifier, publicDir) {\n  const { existsSync } = require('node:fs');\n  const path = require('node:path');\n  return specifier.startsWith('/') && existsSync(path.join(publicDir, specifier));\n}","tryCatchPattern":null,"preventionTips":["Keep JS/CSS/JSON modules in `src`, not `public`.","Reference `public` files only via HTML attributes or runtime `fetch`.","Use the `?url` suffix when you need a public file's URL from JS."],"tags":["public","import","asset","config"],"analyzedSha":"89620f09afcfef6b35e7bb8660132ab5b4d0cd3b","analyzedAt":"2026-08-03T19:28:02.920Z","schemaVersion":2}