spree/spree · error · Error
Nav entry "${key}" not found.
Error message
Nav entry "${key}" not found. What it means
nav.update(key, patch) requires an existing top-level entry; when no registered entry matches the key it throws before mutating anything. The registry fails loudly rather than no-op'ing so a typo'd or stale key surfaces immediately, and the entry list stays consistent for listeners.
Source
Thrown at packages/dashboard-core/src/lib/nav-registry.ts:120
// ============================================================================
// Public API
// ============================================================================
export const nav: NavMutator = {
add(entry) {
ensureUniqueKey(entry.key)
entries.push(entry)
notify()
},
remove(key) {
const i = findIndex(key)
if (i === -1) return
entries.splice(i, 1)
notify()
},
update(key, patch) {
const e = entries.find((x) => x.key === key)
if (!e) throw new Error(`Nav entry "${key}" not found.`)
Object.assign(e, patch)
notify()
},
insertBefore(targetKey, entry) {
const i = findIndex(targetKey)
if (i === -1) throw new Error(`Nav entry "${targetKey}" not found.`)
ensureUniqueKey(entry.key)
// Inherit target's position so the relative order survives `getNavEntries`'s sort.
const target = entries[i]
const adjusted: NavEntry = {
...entry,
position: entry.position ?? (target.position ?? 100) - 1,
}
entries.splice(i, 0, adjusted)
notify()
},
insertAfter(targetKey, entry) {
const i = findIndex(targetKey)View on GitHub (pinned to 06bf66a868)
Solutions
- Read the exact keys from the current nav snapshot (getNavEntries) and match the casing
- Defer the update until after app boot when patching built-ins, so they have registered first
- Guard the update with an existence check and fall back to add() when the entry is genuinely absent
Example fix
// before
nav.update('product', { label: 'Catalog' }) // throws: not found
// after
nav.update('products', { label: 'Catalog' }) Defensive patterns
Strategy: validation
Validate before calling
const exists = getNavEntries().main.some((e) => e.key === 'products')
if (exists) {
nav.update('products', patch)
} else {
nav.add({ key: 'products', ...patch })
} Try / catch
try {
nav.update(key, patch)
} catch (err) {
if (err instanceof Error && err.message.includes('not found')) {
nav.add({ key, ...patch })
} else {
throw err
}
} Prevention
- Derive keys from getNavEntries() rather than hardcoding
- Defer built-in patching to post-boot hooks
- Pin plugin versions against dashboards whose nav keys you've verified
- Type keys as literal unions to catch typos at compile time
When it happens
Trigger: update('product', ...) misspelling the built-in 'products'; the target entry removed by another plugin before yours updates; a key renamed in a newer dashboard version; plugin init running before built-ins register.
Common situations: Key copied from outdated docs or a different dashboard version; load-order changes after a plugin update; another plugin conditionally removing the section you patch.
Related errors
- Nav entry "${targetKey}" not found.
- Nav child "${childKey}" not found under "${parentKey}".
- Create action "${key}" not found.
- Nav entry "${key}" already registered. Use nav.update() inst
- Nav child "${child.key}" already exists under "${parentKey}"
AI-assisted analysis of spree/spree@06bf66a868 (2026-08-21).
Data as JSON: /api/errors/e02f31154494306b.
Report an issue: GitHub.