{"record":{"id":"bcbdcc385f4317dc","repo":"Crosstalk-Solutions/project-nomad","slug":"custom-library-not-found-bcbdcc","errorCode":null,"errorMessage":"Custom library not found","messagePattern":"Custom library not found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"admin/app/controllers/zim_controller.ts","lineNumber":210,"sourceCode":"    try {\n      const source = await this.zimService.addCustomLibrary(payload.name, payload.base_url)\n      return { message: 'Custom library added', library: source }\n    } catch (error) {\n      if (error.message === 'Maximum of 10 custom libraries allowed') {\n        return response.status(400).send({ message: error.message })\n      }\n      throw error\n    }\n  }\n\n  async removeCustomLibrary({ request, response }: HttpContext) {\n    const payload = await request.validateUsing(idParamValidator)\n    try {\n      await this.zimService.removeCustomLibrary(payload.params.id)\n      return { message: 'Custom library removed' }\n    } catch (error) {\n      if (error.message === 'Custom library not found') {\n        return response.status(404).send({ message: error.message })\n      }\n      throw error\n    }\n  }\n\n  async browseLibrary({ request, response }: HttpContext) {\n    const payload = await request.validateUsing(browseLibraryValidator)\n    try {\n      return await this.zimService.browseLibraryUrl(payload.url)\n    } catch (error) {\n      if (error.message?.includes('loopback or link-local')) {\n        return response.status(400).send({ message: error.message })\n      }\n      return response.status(502).send({\n        message: 'Could not fetch directory listing from the provided URL',\n      })\n    }\n  }","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/Crosstalk-Solutions/project-nomad/blob/0bd1c6f4f9888d577fe232de06ac144bb8337131/admin/app/controllers/zim_controller.ts#L192-L228","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fetch the current custom library list and use an id from that fresh response","Treat 404 as success if your goal is removal (the library is already gone) — make the delete idempotent client-side","If the id comes from UI state, refresh the list after every mutation to avoid stale ids"],"exampleFix":"// before\nawait api.delete(`/zim/libraries/${staleId}`) // 404\n// after\nconst libs = await api.get('/zim/libraries')\nconst target = libs.find(l => l.name === 'my-lib')\nif (target) await api.delete(`/zim/libraries/${target.id}`)","handlingStrategy":"try-catch","validationCode":"const libs = await api.get('/zim/libraries')\nif (!libs.some(l => l.id === id)) return // already gone — nothing to do\nawait api.delete(`/zim/libraries/${id}`)","typeGuard":null,"tryCatchPattern":"try {\n  await api.delete(`/zim/libraries/${id}`)\n} catch (e) {\n  if (e.status === 404) return // treat as success: already removed\n  throw e\n}","preventionTips":["Make delete idempotent in the client","Always derive ids from a fresh list fetch","Refresh UI state after every mutation"],"tags":["zim","custom-library","not-found","http-404","idempotency"],"backgroundTag":"resource-not-found","analyzedSha":"0bd1c6f4f9888d577fe232de06ac144bb8337131","analyzedAt":"2026-08-27T05:34:15.424Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}