{"record":{"id":"9de6456ba99020af","repo":"withastro/astro","slug":"apps-must-be-an-object-with-an-id-a-name-and-an-e","errorCode":null,"errorMessage":"Apps must be an object with an id, a name and an entrypoint.","messagePattern":"Apps must be an object with an id, a name and an entrypoint\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts","lineNumber":102,"sourceCode":"\t\t\t\t\t\t\t\t\t\t)})`,\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t.join(',')}]));\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tasync function safeLoadPlugin(appDefinition, importEntrypoint, entrypoint) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tlet app;\n\t\t\t\t\t\t\t\tif (typeof appDefinition === 'string') {\n\t\t\t\t\t\t\t\t\tapp = await importEntrypoint();\n\n\t\t\t\t\t\t\t\t\tif (typeof app !== 'object' || !app.id || !app.name) {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Apps must default export an object with an id, and a name.\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tapp = appDefinition;\n\n\t\t\t\t\t\t\t\t\tif (typeof app !== 'object' || !app.id || !app.name || !app.entrypoint) {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"Apps must be an object with an id, a name and an entrypoint.\");\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tconst loadedApp = await importEntrypoint();\n\n\t\t\t\t\t\t\t\t\tif (typeof loadedApp !== 'object') {\n\t\t\t\t\t\t\t\t\t\tthrow new Error(\"App entrypoint must default export an object.\");\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tapp = { ...app, ...loadedApp };\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn app;\n\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\tconsole.error(\\`Failed to load dev toolbar app from \\${entrypoint}: \\${err.message}\\`);\n\n\t\t\t\t\t\t\t\tif (import.meta.hot) {\n\t\t\t\t\t\t\t\timport.meta.hot.send('astro:devtoolbar:error:load', { entrypoint: entrypoint, error: err.message })\n\t\t\t\t\t\t\t\t}","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/toolbar/vite-plugin-dev-toolbar.ts#L84-L120","documentation":"Thrown by the Astro dev toolbar Vite plugin when a registered toolbar app is provided as an object (the structured form) but is missing one of the three required fields: `id`, `name`, or `entrypoint`. The object form is the alternative to passing a bare entrypoint string; it lets you declare metadata separately from the entrypoint module. All three fields are mandatory in this form because the toolbar needs the id and name before the entrypoint is even imported.","triggerScenarios":"Calling `addDevToolbarApp()` (or configuring `devToolbarApps`) with an object like `{ id: 'x' }`, `{ id: 'x', name: 'Y' }` (no entrypoint), or `{ name: 'Y', entrypoint: '/path' }` (no id). The check runs at toolbar-app load time inside the generated `safeLoadPlugin` virtual module: `typeof app !== 'object' || !app.id || !app.name || !app.entrypoint`.","commonSituations":"Writing a custom dev toolbar app integration and forgetting the `entrypoint` field (most common omission, since id/name feel obvious). Migrating an app from the string shorthand to the object form and dropping a field. Typos in field names (e.g. `entry` instead of `entrypoint`).","solutions":["Ensure the object passed to addDevToolbarApp includes all three keys: { id: 'my-app', name: 'My App', entrypoint: 'virtual:my-app' }.","If you only have an entrypoint module that default-exports { id, name }, pass it as a plain string instead of an object — the string branch only requires id and name on the exported object.","Check that `id` is a non-empty unique string and `name` is a non-empty human-readable string; falsy values (empty string, 0) fail the guard."],"exampleFix":"// before\nastroConfig.devToolbarApps = [{ id: 'myapp', name: 'My App' }];\n\n// after\nastroConfig.devToolbarApps = [{ id: 'myapp', name: 'My App', entrypoint: '/src/toolbar/myapp.ts' }];","handlingStrategy":"validation","validationCode":"function isValidToolbarApp(app) {\n  if (typeof app === 'string') return app.length > 0;\n  return typeof app === 'object' && app !== null\n    && typeof app.id === 'string' && app.id.length > 0\n    && typeof app.name === 'string' && app.name.length > 0\n    && (typeof app.entrypoint === 'string' || app.entrypoint instanceof URL);\n}\nif (!isValidToolbarApp(myApp)) throw new Error('Invalid toolbar app shape');","typeGuard":"function isToolbarAppObject(app): app is { id: string; name: string; entrypoint: string | URL } {\n  return typeof app === 'object' && app !== null\n    && typeof app.id === 'string' && typeof app.name === 'string'\n    && (typeof app.entrypoint === 'string' || app.entrypoint instanceof URL);\n}","tryCatchPattern":null,"preventionTips":["Always specify id, name, AND entrypoint when using the object form for addDevToolbarApp.","Prefer the string shorthand when the entrypoint module itself exports id and name.","Add a unit test asserting your integration's devToolbarApps array passes a shape validator."],"tags":["dev-toolbar","configuration","validation"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}