{"record":{"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/b4d66fee14d970f45b8a6f3d7d6aee73ca9b88ab/packages/vite/src/node/plugins/importAnalysis.ts#L560-L596","documentation":"importAnalysis rejects bare imports of files that live in the /public directory. Public files are served/copied verbatim and are not module-graph entries, so importing them as JS/CSS would break the build. The guard checks specifiers starting with '/' that resolve via checkPublicFile and are not assets (assetsInclude) or already ?url-qualified.","triggerScenarios":"Code does `import x from '/data.json'` or `import './public/script.js'`-style references where the target is actually in the public dir. The check fires when specifier starts with '/', resolves to a public file, and is not classed as an asset or marked with ?url.","commonSituations":"Developers putting shared runtime JS/JSON in /public intending to import it, or moving a file into /public without updating imports. Also triggered by absolute '/...' imports that coincidentally match a public filename.","solutions":["Move the file out of /public into src (or another source dir) so it becomes a normal module.","If you only need the URL, import with the ?url suffix: `import url from '/file.js?url'` (the message suggests injectQuery(specifier, 'url')).","Reference public JS/CSS via <script src>/<link href> in index.html instead of importing them.","If the file is genuinely an asset, ensure config.assetsInclude matches its extension so the guard treats it as an asset."],"exampleFix":"// before\nimport data from '/config.json'\n// after — move config.json to src/, then\nimport data from './config.json'\n// or just get the URL\nimport dataUrl from '/config.json?url'","handlingStrategy":"validation","validationCode":"// Lint source for imports that resolve into /public\nimport { checkPublicFile } from 'vite'\nfunction isPublicImport(specifier, config) {\n  return specifier[0] === '/' && checkPublicFile(specifier, config)\n    && !config.assetsInclude(specifier) && !specifier.includes('?url')\n}\nif (isPublicImport(specifier, config)) throw new Error('Use ?url or move out of /public')","typeGuard":"function isPublicNonAsset(specifier, config): boolean {\n  return specifier.startsWith('/')\n    && checkPublicFile(specifier, config) !== undefined\n    && !config.assetsInclude(specifier.replace(/\\?.*$/, ''))\n    && !/\\?url$/.test(specifier)\n}","tryCatchPattern":null,"preventionTips":["Keep importable modules under src; reserve /public for verbatim assets only.","Reference public JS/CSS via HTML tags, not import statements.","Use the ?url suffix when you need a public file's URL in JS."],"tags":["public","imports","assets","import-analysis"],"backgroundTag":null,"analyzedSha":"b4d66fee14d970f45b8a6f3d7d6aee73ca9b88ab","analyzedAt":"2026-08-11T11:49:19.515Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}