{"record":{"id":"83c765cd8958c2b7","repo":"spree/spree","slug":"slot-entry-name-id-not-found","errorCode":null,"errorMessage":"Slot entry \"${name}#${id}\" not found.","messagePattern":"Slot entry \"(.+?)#(.+?)\" not found\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/dashboard-core/src/lib/slot-registry.ts","lineNumber":85,"sourceCode":"\nexport function removeSlot(name: string, id: string): void {\n  const list = registry.get(name)\n  if (!list) return\n  const next = list.filter((e) => e.id !== id)\n  if (next.length === list.length) return\n  registry.set(name, next)\n  notify()\n}\n\nexport function updateSlot<TContext = unknown>(\n  name: string,\n  id: string,\n  patch: Partial<Omit<SlotEntry<TContext>, 'id'>>,\n): void {\n  const list = registry.get(name)\n  const entry = list?.find((e) => e.id === id)\n  if (!list || !entry) {\n    throw new Error(`Slot entry \"${name}#${id}\" not found.`)\n  }\n  registry.set(\n    name,\n    list.map((e) => (e.id === id ? ({ ...e, ...patch } as SlotEntry) : e)),\n  )\n  notify()\n}\n\n/**\n * Subscribe to a slot's entries. Re-renders when entries are added/removed/updated\n * so plugins registered after first render still appear.\n *\n * Returned entries are sorted by position (default 100) and stable within ties.\n * Filtering by `if` happens at render time inside <Slot> — the hook returns the\n * full registered list so the caller can decide how to combine context.\n */\nexport function useSlotEntries(name: string): readonly SlotEntry[] {\n  const entries = useSyncExternalStore(","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/spree/spree/blob/06bf66a8684b9de03210bbb2ddc8c1f5ba522fa2/packages/dashboard-core/src/lib/slot-registry.ts#L67-L103","documentation":"Thrown by updateSlot(name, id, patch) in @spree/dashboard-core when the slot registry has no entry with that id in the named slot — either the slot name is unknown or the id was never registered (registerSlot creates entries; updateSlot only patches). The message identifies the entry as \"name#id\" so you can see which half of the pair failed.","triggerScenarios":"Slot-name typos ('product.form-sidebar' vs 'product.form_sidebar'); id typos or case mismatch; calling updateSlot before the entry was registered; updating an entry a newer dashboard version renamed or removed; updating after removeSlot.","commonSituations":"Plugins patching built-in slot entries by guessed names/ids; dashboard upgrades reorganizing built-in slots; string constants for slot names drifting between packages.","solutions":["Verify the slot name and id exactly match the registerSlot call (built-ins are declared in the dashboard shell — check its source for the canonical name)","Track the ids you registered and only update those","Use an upsert helper: catch not-found and fall back to registerSlot with a full entry","Export shared constants for slot names from one module instead of repeating string literals"],"exampleFix":"// before\nupdateSlot('product.form-sidebar', 'acme.wishlist-count', { position: 10 }) // slot name typo\n\n// after\nupdateSlot('product.form_sidebar', 'acme.wishlist-count', { position: 10 })\n// upsert if the entry may not exist yet\ntry {\n  updateSlot('product.form_sidebar', 'acme.wishlist-count', patch)\n} catch (err) {\n  if (!(err instanceof Error && err.message.includes('not found'))) throw err\n  registerSlot('product.form_sidebar', { id: 'acme.wishlist-count', component: WishlistCount, ...patch })\n}","handlingStrategy":"validation","validationCode":"// Track your own slot ids; never updateSlot a pair you did not register.\nimport { registerSlot, updateSlot } from '@spree/dashboard-core'\n\nconst mySlotIds = new Set<string>()\n\nexport function registerMySlot(name: string, entry: SlotEntry): void {\n  registerSlot(name, entry)\n  mySlotIds.add(`${name}#${entry.id}`)\n}\n\nexport function updateMySlot(name: string, id: string, patch: Partial<SlotEntry>): void {\n  if (!mySlotIds.has(`${name}#${id}`)) return\n  updateSlot(name, id, patch)\n}","typeGuard":null,"tryCatchPattern":"// Upsert: register the full entry when the patch target is missing.\ntry {\n  updateSlot(name, id, patch)\n} catch (err) {\n  if (!(err instanceof Error && err.message.includes('not found'))) throw err\n  registerSlot(name, { id, component: FallbackComponent, ...patch } as SlotEntry)\n}","preventionTips":["Export slot-name constants from a shared module instead of repeating literals","Verify built-in slot names/ids against the dashboard shell source before patching","Remember the registry key is the pair name#id — a correct id with a wrong slot name still throws"],"tags":["dashboard-plugin","slots","registry","key-not-found"],"backgroundTag":"registry-key-not-found","analyzedSha":"06bf66a8684b9de03210bbb2ddc8c1f5ba522fa2","analyzedAt":"2026-08-21T14:59:48.125Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}