Skip to content

fix(assets): pass through images Sharp cannot decode instead of crashing#16451

Open
maximslo wants to merge 6 commits intowithastro:mainfrom
maximslo:fix/animated-avif-passthrough
Open

fix(assets): pass through images Sharp cannot decode instead of crashing#16451
maximslo wants to merge 6 commits intowithastro:mainfrom
maximslo:fix/animated-avif-passthrough

Conversation

@maximslo
Copy link
Copy Markdown
Contributor

@maximslo maximslo commented Apr 23, 2026

This is a fix for #16267.

Changes

  • Wrapped Sharp's metadata() call in a try/catch. When Sharp cannot decode an image (e.g. animated AVIF sequences), the original buffer is returned unmodified instead of crashing the build. The file is still hashed for cache-busting; only transformation is skipped.
    • Those bytes then get written to _astro/filename.avif in the build output. The <img> tag in the HTML still points to that file; the browser receives the original animated AVIF directly.
    • The only difference from a normal pass-through (like public/) is that Astro still hashes the filename for cache-busting. The content is identical to the source file.

Testing

Run the new test directly:
pnpm run test:match "animated avif"

OR manually:

  1. Add your own animated AVIF to packages/astro/test/fixtures/core-image-ssg/src/assets/
  2. Reference it via <Image> in a page.
  3. Run pnpm build

The build should complete and the file should appear in _astro/ unmodified. Previously this would crash with CouldNotTransformImage.

Note: future support for AVIF would require...

The groundwork is already partly there. pages: -1 is the Sharp flag that loads all frames of an animated image (already set) BUT needed:

  • Sharp/libvips version support. The crash itself (metadata() throwing) means the current Sharp (0.34.5) can't decode animated AVIF metadata. This is a libvips limitation, newer builds of libvips with libaom support can handle it. Depends on Sharp upstream.

  • Format detection. AVIF uses the HEIF container, so Sharp reports animated AVIF as format: 'heif', not avif. The animated GIF → WebP path in sharp.ts would need an equivalent:

if (transform.format === 'webp' && (format === 'gif' || format === 'heif')) {
    result.webp({ ...encoderOptions, loop: 0 });
}
  • Animated AVIF output. result.avif() with pages: -1 already loaded should output animated AVIF, but only once Sharp can decode it in the first place.

So this pass-through fix is fine short-term. Full support is blocked on Sharp's libvips gaining animated AVIF decode support.

Docs

/cc @withastro/maintainers-docs for feedback!

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 23, 2026

🦋 Changeset detected

Latest commit: 0d3f832

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added the pkg: astro Related to the core `astro` package (scope) label Apr 23, 2026
@maximslo maximslo marked this pull request as ready for review April 23, 2026 03:55
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 23, 2026

Merging this PR will not alter performance

✅ 18 untouched benchmarks


Comparing maximslo:fix/animated-avif-passthrough (0d3f832) with main (1058428)

Open in CodSpeed

Copy link
Copy Markdown
Contributor

@fkatsuhiro fkatsuhiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maximslo
Thank you for your PR!
It seems faild some tests. I think it related new imolementation of this PR.
Will you check this? And I recomend you to create test for this issue. Thank you!

@maximslo
Copy link
Copy Markdown
Contributor Author

@fkatsuhiro Added! You can run it with pnpm run test:match "animated avif"

I believe all earlier failing tests were MiniflareCoreError [ERR_RUNTIME_FAILURE], the Cloudflare Workers runtime (infrastructure/environment issue with the CI runner, not anything touched by my change)

@maximslo maximslo requested a review from fkatsuhiro April 24, 2026 03:29
Copy link
Copy Markdown
Contributor

@fkatsuhiro fkatsuhiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maximslo san
Thank you for your updating!
I think it looks good to me. But I forgot to tell you to create changeset file. Sorry.

You can create changeset file via command pnpm changeset. And select your updated package and write summery of your change. Could you update your PR?
Reference: https://contribute.docs.astro.build/docs-for-code-changes/changesets/#tips-and-examples

Sorry for bothering.

@maximslo maximslo requested a review from fkatsuhiro April 24, 2026 18:32
Copy link
Copy Markdown
Contributor

@fkatsuhiro fkatsuhiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your updating!
Looks good to me! Thanks!

@matthewp
Copy link
Copy Markdown
Contributor

What does have a passthrough achieve? if you have animated avif that isn't supported, you just get an unoptimized version, right? But in this case, can't you just not use the Image component for this?

Not 100% against this, just my initial thought is that it might be better to through, because by using Image you are asking for optimizations, and not getting them might be more confusing than throughing (in which case you can just avoid using the Image component).

@matthewp matthewp assigned matthewp and unassigned matthewp Apr 27, 2026
@maximslo
Copy link
Copy Markdown
Contributor Author

@matthewp Yes, users could just use <img> instead. But consistency and future-proofing are the benefits. Plus passthrough is how we already handle other unsupported formats (SVG).

Without this fix: The build crashes (exit code 1) so they have no choice. Must either delete the image, convert it, or move it to public/.

To address your concern with the fix, I'd suggest logging a warning for both SVG and AVIF:

catch {
  console.warn(
    `⚠️  Astro could not optimize image "${transform.src}". ` +
    `Sharp doesn't support this format. The image will be used unoptimized. ` +
    `Consider converting to WebP or placing in the public/ folder.`
  );
  return { data: inputBuffer, format: transform.format };
}
// Return SVGs as-is
if (transform.format === 'svg') {
  console.debug(
    `⚠️ SVG image "${transform.src}" will not be optimized. ` +
    `Sharp has limited SVG support.`
  );
  return { data: inputBuffer, format: 'svg' };
}

With this fix + warning: The build succeeds and users see a clear warning message that tells them:

  • The image wasn't optimized
  • Suggests solutions: convert to WebP, move to public/, or use a regular <img> tag

Some might prefer to convert the AVIF and use <Image> for consistency; others might prefer a regular <img> tag. Either way, they can now deploy.

I think warning + passing through is better UX than crashing, because it unblocks the user, and a warning would clear up any confusion/inform them of the issue.

@matthewp
Copy link
Copy Markdown
Contributor

Pinging @Princesseuh for her opinion on this.

@Princesseuh
Copy link
Copy Markdown
Member

I think with a warning it's okay. Ultimately we wouldn't hide the problem like this, but since there's no real solution on the user side (because in SSR, remote images, uploads, etc you might not be able to know what you're processing), a warning and not crashing seems like a better UX, I agree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: astro Related to the core `astro` package (scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants