withastro/astro · error · Error

Apps must default export an object with an id, and a name.

Error message

Apps must default export an object with an id, and a name.

What it means

The dev-toolbar Vite plugin loads each registered toolbar app. When an app is registered by string entrypoint, Astro dynamically imports it and expects the module to default-export an object containing at least `id` and `name`. If the imported value is not an object, or lacks `id`/`name`, the plugin throws inside the dev server so the author fixes the app's contract. (The object-form registration additionally requires `entrypoint`.)

Source

Thrown at packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts:96

										`safeLoadPlugin(${JSON.stringify(
											plugin,
										)}, async () => (await import(${JSON.stringify(
											typeof plugin === 'string' ? plugin : plugin.entrypoint.toString(),
										)})).default, ${JSON.stringify(
											typeof plugin === 'string' ? plugin : plugin.entrypoint.toString(),
										)})`,
								)
								.join(',')}]));
						};

						async function safeLoadPlugin(appDefinition, importEntrypoint, entrypoint) {
							try {
								let app;
								if (typeof appDefinition === 'string') {
									app = await importEntrypoint();

									if (typeof app !== 'object' || !app.id || !app.name) {
										throw new Error("Apps must default export an object with an id, and a name.");
									}
								} else {
									app = appDefinition;

									if (typeof app !== 'object' || !app.id || !app.name || !app.entrypoint) {
										throw new Error("Apps must be an object with an id, a name and an entrypoint.");
									}

									const loadedApp = await importEntrypoint();

									if (typeof loadedApp !== 'object') {
										throw new Error("App entrypoint must default export an object.");
									}

									app = { ...app, ...loadedApp };
								}

								return app;

View on GitHub (pinned to d081033d5f)

Solutions

  1. Make the entrypoint default-export an object with `id` and `name` (string form), e.g. `export default { id: 'my-app', name: 'My App', ... }`.
  2. If registering via the object form, also include `entrypoint`.
  3. Verify the entrypoint path resolves to the intended module and that the module loads without error.

Example fix

// before — entrypoint has no valid default export
// src/my-app.ts
export function init() {}

// after
// src/my-app.ts
export default {
  id: 'my-app',
  name: 'My App',
  // ...app implementation
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate a toolbar app's default export shape before registration
interface ToolbarApp { id: string; name: string; entrypoint?: string }
function assertToolbarApp(mod: unknown): asserts mod is ToolbarApp {
  if (typeof mod !== 'object' || mod === null || !('id' in mod) || !('name' in mod)) {
    throw new Error('Toolbar app must default export an object with id and name');
  }
}

Type guard

const isToolbarApp = (m: unknown): m is ToolbarApp =>
  typeof m === 'object' && m !== null && typeof (m as any).id === 'string' && typeof (m as any).name === 'string';

Prevention

When it happens

Trigger: A toolbar app entrypoint module has no default export, exports a non-object, or its default export is missing `id` or `name`; an integration's `addClientDirective`/toolbar registration points at the wrong file; the entrypoint throws during import so its default resolves to undefined.

Common situations: Authoring a custom dev-toolbar app/integration and forgetting the required export shape; pointing the app entrypoint at a non-app module; integration misconfiguration during local development.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/6a274a24aebb7c16. Report an issue: GitHub.