withastro/astro · error · Error

Invalid component export path

Error message

Invalid component export path: ${componentExport}

What it means

Client-side hydration guard inside the <astro-island> custom element. After the component module loads, the element resolves the component-export attribute (default 'default'). A plain export name that is exactly one of the forbidden keys ('__proto__', 'constructor', 'prototype') is rejected before lookup, because resolving it would touch Object prototype members. This is a prototype-pollution protection for the browser hydration runtime.

Solutions

  1. Rename the export in the component module and rebuild so the attribute references a valid export name
  2. Stop hand-writing or mutating <astro-island> markup; let Astro generate it
  3. Audit any HTML post-processing plugins (minifiers, CDN rewriters) that touch component-export attributes

Example fix

// before (hand-authored island markup)
<astro-island component-export="constructor" ...></astro-island>

// after
<astro-island component-export="MyWidget" ...></astro-island>
Defensive patterns

Strategy: validation

Validate before calling

const FORBIDDEN = new Set(['__proto__', 'constructor', 'prototype']);
const exportName = islandEl.getAttribute('component-export') ?? 'default';
if (FORBIDDEN.has(exportName)) throw new Error('Refusing forbidden export name: ' + exportName);

Type guard

function isSafeExportName(name: string): boolean {
  return !['__proto__', 'constructor', 'prototype'].includes(name);
}

Try / catch

If generating astro-island markup programmatically, wrap attribute assignment in try/catch and log the offending component-export value; hydration errors are already caught internally by handleHydrationError, so surface them via an error boundary or console handler instead of crashing.

Prevention

When it happens

Trigger: The component-export attribute of an astro-island element equals '__proto__', 'constructor', or 'prototype'. Reachable via hand-edited or tampered built HTML, a plugin/proxy rewriting island attributes, or a component module that literally exports a member named 'constructor' or 'prototype' and is referenced by that name.

Common situations: Almost never occurs in a normal build; seen when output HTML is mutated by optimizers/minifiers/security appliances, when islands are hand-authored, or when pen-testing tooling injects crafted attribute values.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/543146647a68ee00. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/runtime/server/astro-island.ts:160

			if (Astro[directive] === undefined) {
				window.addEventListener(`astro:${directive}`, () => this.start(), { once: true });
				return;
			}
			try {
				await Astro[directive]!(
					async () => {
						const rendererUrl = this.getAttribute('renderer-url');
						try {
							const [componentModule, { default: hydrator }] = await Promise.all([
								this.importWithRetry(this.getAttribute('component-url')!),
								rendererUrl
									? this.importWithRetry(rendererUrl)
									: Promise.resolve({ default: () => () => {} }),
							]);
							const componentExport = this.getAttribute('component-export') || 'default';
							if (!componentExport.includes('.')) {
								if (FORBIDDEN_COMPONENT_EXPORT_KEYS.has(componentExport)) {
									throw new Error(`Invalid component export path: ${componentExport}`);
								}
								this.Component = componentModule[componentExport];
							} else {
								this.Component = componentModule;
								for (const part of componentExport.split('.')) {
									if (
										FORBIDDEN_COMPONENT_EXPORT_KEYS.has(part) ||
										!this.Component ||
										(typeof this.Component !== 'object' && typeof this.Component !== 'function') ||
										!Object.hasOwn(this.Component, part)
									) {
										throw new Error(`Invalid component export path: ${componentExport}`);
									}
									this.Component = this.Component[part];
								}
							}
							this.hydrator = hydrator;
							return this.hydrate;

View on GitHub (pinned to 52e6c34790)