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

  1. Read the exact keys from the current nav snapshot (getNavEntries) and match the casing
  2. Defer the update until after app boot when patching built-ins, so they have registered first
  3. 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

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


AI-assisted analysis of spree/spree@06bf66a868 (2026-08-21). Data as JSON: /api/errors/e02f31154494306b. Report an issue: GitHub.