{"record":{"id":"45cb7f34480b75aa","repo":"paperclipai/paperclip","slug":"packagename-is-required-and-must-be-a-string","errorCode":null,"errorMessage":"packageName is required and must be a string.","messagePattern":"packageName is required and must be a string\\.","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"server/src/routes/adapters.ts","lineNumber":309,"sourceCode":"  });\n\n  /**\n   * POST /api/adapters/install\n   *\n   * Install an external adapter from an npm package or local path.\n   *\n   * Request body:\n   * - packageName: string (required) — npm package name or local path\n   * - isLocalPath?: boolean (default false)\n   * - version?: string — target version for npm packages\n   */\n  router.post(\"/adapters/install\", async (req, res) => {\n    assertInstanceAdmin(req);\n    assertAdapterCodeInstallAllowed();\n    assertAdapterManagementVisible();\n\n    const { packageName, isLocalPath = false, version } = req.body as AdapterInstallRequest;\n\n    if (!packageName || typeof packageName !== \"string\") {\n      res.status(400).json({ error: \"packageName is required and must be a string.\" });\n      return;\n    }\n\n    // Strip version suffix if the UI sends \"pkg@1.2.3\" instead of separating it\n    // e.g. \"@henkey/hermes-paperclip-adapter@0.3.0\" → packageName + version\n    let canonicalName = packageName;\n    let explicitVersion = version;\n    const versionSuffix = packageName.match(/@(\\d+\\.\\d+\\.\\d+.*)$/);\n    if (versionSuffix) {\n      // For scoped packages: \"@scope/name@1.2.3\" → \"@scope/name\" + \"1.2.3\"\n      // For unscoped: \"name@1.2.3\" → \"name\" + \"1.2.3\"\n      const lastAtIndex = packageName.lastIndexOf(\"@\");\n      if (lastAtIndex > 0 && !explicitVersion) {\n        canonicalName = packageName.slice(0, lastAtIndex);\n        explicitVersion = versionSuffix[1];\n      }","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/paperclipai/paperclip/blob/5716fe907e596ce73501408fc6efdb19fb61edf2/server/src/routes/adapters.ts#L291-L327","documentation":"Body-shape guard on POST /api/adapters/install: the request body lacks a usable packageName (missing, empty, or non-string), so the install cannot determine which npm package or local path to install and the route returns 400 before any install attempt.","triggerScenarios":"Thrown at server/src/routes/adapters.ts:258 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Provide packageName as a string in the request body."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"5716fe907e596ce73501408fc6efdb19fb61edf2","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}