withastro/astro · error · AstroError

UnsupportedConfigTransformError

UnsupportedConfigTransformError

Error message

`transform()` functions in your content config must return valid JSON, or data types compatible with the devalue library (including Dates, Maps, and Sets).
Full error: ${parseError}

What it means

Collection entries are serialized with devalue so they can be stored in the data store and re-hydrated inside generated modules. A transform() function in your loader config must return JSON-compatible values (plus devalue-supported types like Date, Map, Set). When devalue's stringify encounters something it cannot handle — a function, symbol, class instance, or undefined inside an array — the error is wrapped as UnsupportedConfigTransformError with the parse error attached.

Solutions

  1. Map class instances to plain objects and strip functions before returning from transform().
  2. Convert wrapped types to primitives or built-ins: Moment to Date, Decimal to string, URL is supported natively.
  3. Add a unit test that runs devalue.stringify() over your transform output (see validation below).

Example fix

// before
loader: {
  async load() {},
  transform(entry) {
    return { ...entry, published: new Moment(entry.date), href: (slug) => `/posts/${slug}` };
  },
}

// after
loader: {
  async load() {},
  transform(entry) {
    return { ...entry, published: new Date(entry.date) };
  },
}
Defensive patterns

Strategy: validation

Validate before calling

// Unit-test transform output with the same serializer Astro uses
import { stringify } from 'devalue';

test('loader transform output is serializable', () => {
  for (const entry of sampleEntries) {
    expect(() => stringify(myTransform(entry))).not.toThrow();
  }
});

Type guard

const isPlainSerializable = (v: unknown): boolean =>
  v === null ||
  ['string', 'number', 'boolean'].includes(typeof v) ||
  v instanceof Date ||
  v instanceof Map ||
  v instanceof Set ||
  (Array.isArray(v) && v.every(isPlainSerializable)) ||
  (typeof v === 'object' && Object.values(v).every(isPlainSerializable));

Prevention

When it happens

Trigger: A transform() returning functions (href: (slug) => ...), symbols, class or model instances, Moment/Luxon wrappers, or undefined stored in arrays; cyclic structures.

Common situations: Returning ORM entities or rich objects from a loader transform; leaving helper functions attached to data; porting code that relied on structuredClone or JSON.stringify semantics that silently dropped functions.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/35d38ff99b58edf0. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/content/vite-plugin-content-imports.ts:435

/** Stringify entry `data` at build time to be used as a Vite module */
function stringifyEntryData(data: Record<string, any>, isSSR: boolean): string {
	try {
		return devalue.uneval(data, (value) => {
			// Add support for URL objects
			if (value instanceof URL) {
				return `new URL(${JSON.stringify(value.href)})`;
			}

			// For Astro assets, add a proxy to track references
			if (typeof value === 'object' && 'ASTRO_ASSET' in value) {
				const { ASTRO_ASSET, ...asset } = value;
				asset.fsPath = ASTRO_ASSET;
				return getProxyCode(asset, isSSR);
			}
		});
	} catch (e) {
		if (e instanceof Error) {
			throw new AstroError({
				...AstroErrorData.UnsupportedConfigTransformError,
				message: AstroErrorData.UnsupportedConfigTransformError.message(e.message),
				stack: e.stack,
			});
		} else {
			throw new AstroError({
				name: 'PluginContentImportsError',
				message: 'Unexpected error processing content collection data.',
			});
		}
	}
}

View on GitHub (pinned to e294953aa8)