Crosstalk-Solutions/project-nomad · warning

Custom library not found

Error message

Custom library not found

What it means

removeCustomLibrary throws when the supplied id does not match any stored custom library source; the controller translates it into a 404. Typically caused by removing an already-deleted library or passing a wrong id.

Source

Thrown at admin/app/controllers/zim_controller.ts:210

    try {
      const source = await this.zimService.addCustomLibrary(payload.name, payload.base_url)
      return { message: 'Custom library added', library: source }
    } catch (error) {
      if (error.message === 'Maximum of 10 custom libraries allowed') {
        return response.status(400).send({ message: error.message })
      }
      throw error
    }
  }

  async removeCustomLibrary({ request, response }: HttpContext) {
    const payload = await request.validateUsing(idParamValidator)
    try {
      await this.zimService.removeCustomLibrary(payload.params.id)
      return { message: 'Custom library removed' }
    } catch (error) {
      if (error.message === 'Custom library not found') {
        return response.status(404).send({ message: error.message })
      }
      throw error
    }
  }

  async browseLibrary({ request, response }: HttpContext) {
    const payload = await request.validateUsing(browseLibraryValidator)
    try {
      return await this.zimService.browseLibraryUrl(payload.url)
    } catch (error) {
      if (error.message?.includes('loopback or link-local')) {
        return response.status(400).send({ message: error.message })
      }
      return response.status(502).send({
        message: 'Could not fetch directory listing from the provided URL',
      })
    }
  }

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Fetch the current custom library list and use an id from that fresh response
  2. Treat 404 as success if your goal is removal (the library is already gone) — make the delete idempotent client-side
  3. If the id comes from UI state, refresh the list after every mutation to avoid stale ids

Example fix

// before
await api.delete(`/zim/libraries/${staleId}`) // 404
// after
const libs = await api.get('/zim/libraries')
const target = libs.find(l => l.name === 'my-lib')
if (target) await api.delete(`/zim/libraries/${target.id}`)
Defensive patterns

Strategy: try-catch

Validate before calling

const libs = await api.get('/zim/libraries')
if (!libs.some(l => l.id === id)) return // already gone — nothing to do
await api.delete(`/zim/libraries/${id}`)

Try / catch

try {
  await api.delete(`/zim/libraries/${id}`)
} catch (e) {
  if (e.status === 404) return // treat as success: already removed
  throw e
}

Prevention

When it happens

Trigger: DELETE to the custom-library route with an id that was never added or was already removed; stale id held by the UI after another client/session removed it.

Common situations: Two admin sessions racing to remove the same library; frontend state out of sync with server after a refresh elsewhere; copy/paste or UUID typo in the id param.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/bcbdcc385f4317dc. Report an issue: GitHub.