{"record":{"id":"493f7d35334516b1","repo":"Budibase/budibase","slug":"invalid-npm-url","errorCode":null,"errorMessage":"Invalid NPM URL","messagePattern":"Invalid NPM URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/api/controllers/plugin/npm.ts","lineNumber":14,"sourceCode":"import { utils as coreUtils } from \"@budibase/backend-core\"\nimport {\n  deleteFolderFileSystem,\n  getPluginMetadata,\n} from \"../../../utilities/fileSystem\"\nimport { join } from \"path\"\nimport { downloadUnzipTarball } from \"./utils\"\n\nfunction parseNpmUrl(url: string): URL {\n  let parsed: URL\n  try {\n    parsed = new URL(url)\n  } catch {\n    throw new Error(\"Invalid NPM URL\")\n  }\n\n  if (parsed.protocol !== \"https:\") {\n    throw new Error(\"The plugin origin must be from NPM\")\n  }\n\n  return parsed\n}\n\nfunction isAllowedNpmHost(host: string): boolean {\n  return host === \"www.npmjs.com\" || host === \"registry.npmjs.org\"\n}\n\nexport async function npmUpload(url: string, name: string, headers = {}) {\n  let npmTarballUrl = url\n  let pluginName = name\n\n  const parsedInput = parseNpmUrl(npmTarballUrl)","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/plugin/npm.ts#L1-L32","documentation":"npm plugin uploads must reference an https URL; parseNpmUrl first ensures the string is a valid URL at all. If new URL() throws (malformed URL), this error is thrown before any host checks.","triggerScenarios":"Calling npmUpload (plugin create with source=NPM) with a value that is not a parseable URL — missing scheme (npmjs.com/package/foo), whitespace, typo like 'https//', or an npm package name instead of a URL.","commonSituations":"Pasting 'npm install @org/pkg' output; supplying 'org/pkg' package name instead of a full URL; trailing spaces from copy/paste; using registry:protocol or git+ssh URLs.","solutions":["Provide a full https URL, e.g. https://www.npmjs.com/package/@org/plugin or a https://registry.npmjs.org/...tgz tarball URL","Fix the scheme typo and ensure no leading/trailing whitespace","Convert a bare package name into https://www.npmjs.com/package/<name>"],"exampleFix":"// before\nawait installPlugin({ source: 'NPM', url: '@org/my-plugin' })\n// after\nawait installPlugin({ source: 'NPM', url: 'https://www.npmjs.com/package/@org/my-plugin' })","handlingStrategy":"validation","validationCode":"function isValidUrl(url: string): boolean {\n  try { new URL(url); return true } catch { return false }\n}\nif (!isValidUrl(url.trim())) throw new Error(`Not a URL: ${url}`)","typeGuard":"function isParseableUrl(url: string): url is string {\n  try { new URL(url); return true } catch { return false }\n}","tryCatchPattern":"try {\n  await installPlugin({ source: 'NPM', url: url.trim() })\n} catch (err) {\n  if (err.message === 'Invalid NPM URL') {\n    console.error(`'${url}' is not a URL — use https://www.npmjs.com/package/<name> or a registry tarball URL`)\n  }\n}","preventionTips":["Always pass a full https:// URL, never a bare package name","Trim whitespace from pasted URLs","Don't paste 'npm install ...' CLI strings as URLs","Convert package names to https://www.npmjs.com/package/<name> programmatically"],"tags":["validation","url","npm"],"backgroundTag":"invalid-url-format","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}