Skip to content

fix(content): don't mutate readonly schemas during image reference processing - #17867

Open
suletetes wants to merge 4 commits into
withastro:mainfrom
suletetes:fix/content-readonly-image-schema
Open

fix(content): don't mutate readonly schemas during image reference processing#17867
suletetes wants to merge 4 commits into
withastro:mainfrom
suletetes:fix/content-readonly-image-schema

Conversation

@suletetes

@suletetes suletetes commented Aug 31, 2026

Copy link
Copy Markdown

What this fixes

Fixes #17793.

Using Zod's .readonly() on an object containing an Astro image() field causes the Content Layer to throw a runtime TypeError during content processing:

The schema itself is valid Zod and parses the content successfully the failure happens afterward, when Astro processes image references.

Root cause

In packages/astro/src/content/mutable-data-store.ts, the collection accessor's set(...) handler walks the parsed data with neotraverse's forEach and calls ctx.update(src) to strip the IMAGE_IMPORT_PREFIX from image src strings. That is an in-place mutation.

When the user's schema uses .readonly(), Zod returns a frozen object (and frozen nested objects), so mutating it directly throws.

The fix

Before traversing/mutating, produce a deep mutable clone with structuredClone(data), run the traversal over the clone, and store the clone in the entry. The user's original (possibly frozen) object is never written to.

This matches the direction suggested in the issue "operate on a mutable copy rather than mutate the object returned by the user's schema." structuredClone is safe here because the content-layer data is plain, devalue-serializable data at this point (it is about to be serialized to disk). The image-prefixing schema-parse path is untouched.

Tests

  • Added a regression test in packages/astro/test/units/content-collections/mutable-data-store.test.ts: "handles frozen data from a .readonly() schema without throwing (issue Zod .readonly() schemas fail during Content Layer image reference processing #17793)." It builds a frozen object (with a nested frozen object) containing image-prefixed values, then asserts that:
    • set(...) does not throw,
    • the stored data has the image prefixes stripped,
    • the user's original frozen object is left untouched,
    • the imageImports paths are recorded correctly.
  • mutable-data-store unit tests: 11/11 pass.
  • All content-collections unit tests: 42/42 pass, no regressions.
  • tsc --noEmit: no errors (structuredClone is a Node 22 global).

A patch changeset is included.

@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8742d5d

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 Aug 31, 2026
@codspeed-hq

codspeed-hq Bot commented Aug 31, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 14.84%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 1 regressed benchmark
✅ 20 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
many-components (markHTMLString, isHTMLString, validateProps) 9.6 ms 11.3 ms -14.84%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing suletetes:fix/content-readonly-image-schema (8742d5d) with main (0389640)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (09d7772) during the generation of this report, so 0389640 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@ematipico ematipico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The tests should use the zod.readonly() bug that was reported.

@suletetes

Copy link
Copy Markdown
Author

The tests should use the zod.readonly() bug that was reported.

updated

@suletetes
suletetes requested a review from ematipico September 4, 2026 22:00
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.

Zod .readonly() schemas fail during Content Layer image reference processing

3 participants