{"record":{"id":"13db732b640047a0","repo":"Mintplex-Labs/anything-llm","slug":"no-plugin-hubid-passed","errorCode":null,"errorMessage":"No plugin hubID passed.","messagePattern":"No plugin hubID passed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/agents/imported.js","lineNumber":138,"sourceCode":"    const currentConfig = safeJsonParse(\n      fs.readFileSync(configLocation, \"utf8\"),\n      null\n    );\n    if (!currentConfig) return;\n\n    const { hubId: _drop, ...safeConfig } = config;\n    const updatedConfig = { ...currentConfig, ...safeConfig };\n    fs.writeFileSync(configLocation, JSON.stringify(updatedConfig, null, 2));\n    return updatedConfig;\n  }\n\n  /**\n   * Deletes a plugin. Removes the entire folder of the object.\n   * @param {string} hubId - The hub ID of the plugin.\n   * @returns {boolean} - True if the plugin was deleted, false otherwise.\n   */\n  static deletePlugin(hubId) {\n    if (!hubId) throw new Error(\"No plugin hubID passed.\");\n    const pluginFolder = path.resolve(pluginsPath, normalizePath(hubId));\n    if (!this.isValidLocation(pluginFolder)) return;\n    fs.rmSync(pluginFolder, { recursive: true });\n    return true;\n  }\n\n  /**\n  /**\n   * Validates if the handler.js file exists for the given plugin.\n   * @param {string} hubId - The hub ID of the plugin.\n   * @returns {boolean} - True if the handler.js file exists, false otherwise.\n   */\n  static validateImportedPluginHandler(hubId) {\n    const handlerLocation = path.resolve(\n      pluginsPath,\n      normalizePath(hubId),\n      \"handler.js\"\n    );","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/526360e320da9d1b36074be5ed64fe76e5bbfbbd/server/utils/agents/imported.js#L120-L156","documentation":"Thrown by ImportedPlugin.deletePlugin(hubId) (server/utils/agents/imported.js:137) when hubId is falsy. The static method is invoked from the DELETE /experimental/agent-plugins/:hubId endpoint, so reaching this throw means the route param was empty/undefined.","triggerScenarios":"DELETE /experimental/agent-plugins/:hubId called with an empty hubId param (e.g. trailing slash, no id), or deletePlugin invoked programmatically with no argument.","commonSituations":"Client sent DELETE to the base path with no id; route mis-registration stripping the param; programmatic caller passed undefined.","solutions":["Ensure the client sends a non-empty hubId in the URL: DELETE /experimental/agent-plugins/<hubId>.","Add a 400 guard in the endpoint when !request.params.hubId before calling deletePlugin."],"exampleFix":"// before\napp.delete('/experimental/agent-plugins/:hubId', (req, res) => {\n  const result = ImportedPlugin.deletePlugin(req.params.hubId);\n// after\napp.delete('/experimental/agent-plugins/:hubId', (req, res) => {\n  if (!req.params.hubId) return res.status(400).json({ error: 'hubId required' });\n  const result = ImportedPlugin.deletePlugin(req.params.hubId);","handlingStrategy":"validation","validationCode":"if (!hubId || typeof hubId !== 'string') return res.status(400).json({ error: 'hubId required' });\nImportedPlugin.deletePlugin(hubId);","typeGuard":null,"tryCatchPattern":"try { ImportedPlugin.deletePlugin(hubId); } catch (e) { if (/No plugin hubID passed/.test(e.message)) return res.status(400).end(); throw e; }","preventionTips":["Validate route params at the endpoint before delegating to the model layer.","Reject empty path segments explicitly."],"tags":["plugins","api","validation","agents"],"backgroundTag":null,"analyzedSha":"526360e320da9d1b36074be5ed64fe76e5bbfbbd","analyzedAt":"2026-08-13T01:45:47.170Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}