Skip to content

astro:assets image service returns 500 in dev when src/assets is a symlink #14937

@alexvas

Description

@alexvas

Astro Info

Astro                    v5.16.3
Vite                     v6.4.1
Node                     v22.21.1
System                   Linux (x64)
Package Manager          npm
Output                   static
Adapter                  none
Integrations             none

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

Description

When src/assets is a symlink, images imported from it and rendered via astro:assets (either <Image> or getImage) fail in dev with a 500 from the /_image endpoint.

The same images, when used via a plain <img src={asset.src}>, work fine in dev.
Build (astro build) also works: the generated HTML references hashed files under /_astro/... and loads correctly.

So the problem appears to be specific to the dev image service + symlinked src/assets.


Minimal reproduction

  1. Create a fresh Astro project (any template is fine):

    npm create astro@latest my-app -- --template minimal --install --no-git --skip-houston
  2. Make src/assets as a symlink:

mkdir shared-assets
cat > shared-assets/logo.svg << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     viewBox="0 0 200 60">
  <rect width="100%" height="100%" fill="none"/>
  <circle cx="30" cy="30" r="20" fill="#1f8ceb"/>
  <path d="M46 22 L62 30 L46 38 Z" fill="#0b6abf" opacity="0.9"/>
  <text x="90" y="36" font-family="sans-serif"
        font-size="20" fill="#111" dominant-baseline="middle">Astro</text>
</svg>

EOF
(cd my-app/src && ln -s ../../shared-assets assets)
  1. Make sure Vite is allowed to read outside the project root if needed, e.g. in astro.config.mjs:
cat > my-app/astro.config.mjs << 'EOF'
import { defineConfig } from "astro/config";

export default defineConfig({
  vite: {
    server: {
      fs: {
        allow: ['.', '../..'],
      },
    },
  },
});
EOF
  1. In src/pages/index.astro add an image imported from src/assets and rendered via astro:assets:
cat > my-app/src/pages/index.astro << 'EOF'
---
import { Image, getImage } from "astro:assets";
import logo from "../assets/logo.svg";
---

<html>
  <body>
    <!-- This one goes through /_image -->
    <Image src={logo} width={140} alt="logo via astro:assets" />

    <!-- This one uses Vite /@fs and works -->
    <img src={logo.src} width="140" alt="logo via img src" />
  </body>
</html>
EOF
  1. Run dev server:
(cd my-app && npm run dev -- --host 0.0.0.0)
  1. Open the page in the browser and check the Network tab.

Expected behavior

  • Both images load successfully in dev.
  • The <Image> (or getImage) call uses the /_image endpoint without errors, even though src/assets is a symlink.

Actual behavior

  • The <img src={logo.src}> version works in dev.

  • The <Image src={logo}> / getImage({ src: logo }) version fails:

    • The browser requests something like:

      <img src="/_image?href=%2F%40fs%2Ftmp%2Ft%2Fshared-assets%2Flogo.svg%..."
      
    • The dev server responds with 500 for that /_image request.

  • No such problem in astro build output: the generated HTML references a static file under /_astro/... and it loads correctly.

So the behavior is inconsistent:

  • dev + symlinked src/assets + astro:assets → 500 from /_image
  • dev + symlinked src/assets + plain <img src={asset.src}> → OK
  • build + symlinked src/assets + astro:assets → OK

Environment

  • Astro: 5.16.3
  • Node: v22.21.1
  • Npm: 10.9.4
  • OS: Linux (symlinks on ext4; also likely reproducible on other Unix-like systems)

Additional context

  • In my real project, src/assets is a symlink to a shared assets folder in a sibling workspace (monorepo).
  • Dev server already allows going up via vite.server.fs.allow = ['.', '../..'].
  • The broken dev URL encodes a path under /@fs/.../src/assets/... into the href query parameter of /_image, which seems to trip whatever path/FS checks the image service uses.
  • Using <img src={asset.src}> everywhere is a viable workaround, but it would be great if astro:assets worked with symlinked src/assets in dev the same way it does in build.
  • The issue seems to be strongly related with the astro:assets image service returns 500 in dev when @assets points to ext. dir #14957

What's the expected result?

  • Both images load successfully in dev.
  • The <Image> (or getImage) call uses the /_image endpoint without errors, even though src/assets is a symlink.

see
https://stackblitz.com/edit/github-fkzyjbhb?file=src%2Fpages%2Findex.astro

N.B. stackblitz does not allow to create symlinks, so there is a ER app there

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-fkzyjbhb?file=src%2Fpages%2Findex.astro

Participation

  • I am willing to submit a pull request for this issue.

Metadata

Metadata

Assignees

Labels

- P3: minor bugAn edge case that only affects very specific usage (priority)feat: assetsRelated to the Assets feature (scope)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions