withastro/astro · error · Error
App entrypoint must default export an object.
Error message
App entrypoint must default export an object.
What it means
Thrown by the dev toolbar plugin when an app is registered in object form (with id/name/entrypoint), the entrypoint is successfully imported, but its default export is not an object. The toolbar merges `{ ...app, ...loadedApp }`, so the loaded entrypoint must contribute an object (typically containing the app's component/hook callbacks).
Source
Thrown at packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts:108
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;
} catch (err) {
console.error(\`Failed to load dev toolbar app from \${entrypoint}: \${err.message}\`);
if (import.meta.hot) {
import.meta.hot.send('astro:devtoolbar:error:load', { entrypoint: entrypoint, error: err.message })
}
return undefined;
}
return undefined;
}View on GitHub (pinned to d081033d5f)
Solutions
- Make the entrypoint default-export an object: `export default { id: 'myapp', name: 'My App', init() {}, ... }`.
- If you intend the default export to be a function/component, switch the registration to the string form so only id/name are validated.
- Verify there are no conditional or named-only exports masking the default export.
Example fix
// before — entrypoint.ts
export default function App() {}
// registration: { id, name, entrypoint: './entrypoint.ts' }
// after
export default { id: 'myapp', name: 'My App', init(canvas) {} }; Defensive patterns
Strategy: validation
Validate before calling
// In the entrypoint module's test:
import * as mod from './my-entrypoint.ts';
if (typeof mod.default !== 'object' || mod.default === null) {
throw new Error('entrypoint must default-export a plain object');
} Type guard
function isAppExport(val): val is { id: string; name: string; [k: string]: unknown } {
return typeof val === 'object' && val !== null && typeof val.id === 'string' && typeof val.name === 'string';
} Prevention
- Default-export a plain object from every toolbar app entrypoint.
- Add an eslint rule or test that asserts the default export is an object literal.
- When refactoring an entrypoint's export shape, update its registration form to match.
When it happens
Trigger: The entrypoint module does `export default function MyComponent() {...}` or `export default class {...}` or has no default export (undefined), then the object-form registration triggers `typeof loadedApp !== 'object'` after `importEntrypoint()`. A default export of a string, number, or function also triggers it.
Common situations: Authoring a toolbar app entrypoint that default-exports a function or component instead of an object. Forgetting the `export default` keyword entirely (loadedApp is undefined). Refactoring an entrypoint from object-export to function-export without updating the registration form.
Related errors
- Apps must be an object with an id, a name and an entrypoint.
- CannotOptimizeSvg
- RemoteImageNotAllowed
- Unknown error parsing tsconfig.json or jsconfig.json. Could
- `--ignore-lock` cannot be used together with `--background`.
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/7e1b862bc0e2a506.
Report an issue: GitHub.