withastro/astro · error · AstroError
LocalImageUsedWrongly
LocalImageUsedWrongly
Error message
`Image`'s and `getImage`'s `src` parameter must be an imported image or a URL, it cannot be a string filepath. Received `${imageFilePath}`. What it means
Thrown by verifyOptions() when src is a string that is not ESM-imported and looks like a local filesystem path. The check fires when the string starts with '/@fs/' or is not remote and does not start with '/'. Passing an absolute/relative filesystem path instead of an import or a URL is rejected.
Source
Thrown at packages/astro/src/assets/services/service.ts:164
// `src` is missing or is `undefined`.
if (!options.src || (!isRemoteImage(options.src) && !isESMImportedImage(options.src))) {
throw new AstroError({
...AstroErrorData.ExpectedImage,
message: AstroErrorData.ExpectedImage.message(
JSON.stringify(options.src),
typeof options.src,
JSON.stringify(options, (_, v) => (v === undefined ? null : v)),
),
});
}
if (!isESMImportedImage(options.src)) {
// User passed an `/@fs/` path or a filesystem path instead of the full image.
if (
options.src.startsWith('/@fs/') ||
(!isRemotePath(options.src) && !options.src.startsWith('/'))
) {
throw new AstroError({
...AstroErrorData.LocalImageUsedWrongly,
message: AstroErrorData.LocalImageUsedWrongly.message(options.src),
});
}
// For remote images, width and height are explicitly required as we can't infer them from the file
let missingDimension: 'width' | 'height' | 'both' | undefined;
if (!options.width && !options.height) {
missingDimension = 'both';
} else if (!options.width && options.height) {
missingDimension = 'width';
} else if (options.width && !options.height) {
missingDimension = 'height';
}
if (missingDimension) {
throw new AstroError({
...AstroErrorData.MissingImageDimension,View on GitHub (pinned to d081033d5f)
Solutions
- Import local images as ESM assets: import img from './img.png'; then <Image src={img} />.
- Place public images under public/ and reference them by root-relative URL starting with '/' (e.g. '/img.png').
- For remote images use the full https:// URL.
- Never pass raw disk paths or '/@fs/' URLs to Image/getImage.
Example fix
// before
<Image src='/@fs/home/user/proj/img.png' />
// after
import img from '../assets/img.png';
<Image src={img} /> Defensive patterns
Strategy: validation
Validate before calling
function isLocalDiskPath(src: string): boolean {
return src.startsWith('/@fs/') || (!/^https?:\/\//.test(src) && !src.startsWith('/'));
} Prevention
- Import local images as ESM assets, never reference disk paths.
- Place public images under public/ and use root-relative URLs.
- Reject '/@fs/' URLs from leaking into production builds.
When it happens
Trigger: Calling the service with options.src equal to '/@fs/abs/path/img.png', a relative path like 'assets/img.png', or 'C:/images/img.png' on Windows — i.e. a local path used where an import or a public-rooted/remote URL is required.
Common situations: User writes <Image src='/path/to/local.png' /> intending a local image instead of `import img from './img.png'`, or pastes an absolute disk path. Also a stray '/@fs/' URL leaking from dev tooling.
Related errors
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/8fd6d46e9be68dd6.
Report an issue: GitHub.