{"record":{"id":"dda45a3501a63d50","repo":"mastra-ai/mastra","slug":"invalid-github-url-specifier","errorCode":null,"errorMessage":"Invalid GitHub URL: ${specifier}","messagePattern":"Invalid GitHub URL: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/plugins/install.ts","lineNumber":228,"sourceCode":"  try {\n    await execa(githubCli, ['--version'], NON_INTERACTIVE_EXEC_OPTIONS);\n  } catch {\n    throw new Error('GitHub CLI is required to install GitHub plugins. Install gh and run gh auth login.');\n  }\n}\n\nasync function assertGithubCliAuthenticated(githubCli: string): Promise<void> {\n  try {\n    await execa(githubCli, ['auth', 'status'], NON_INTERACTIVE_EXEC_OPTIONS);\n  } catch {\n    throw new Error('GitHub CLI is not authenticated. Run gh auth login, then install the plugin again.');\n  }\n}\n\nfunction parseGithubUrl(specifier: string): { owner: string; repo: string; repoSpec: string; ref?: string } {\n  const [urlPart, ref] = specifier.split('#', 2);\n  if (!urlPart) {\n    throw new Error(`Invalid GitHub URL: ${specifier}`);\n  }\n  let url: URL;\n  try {\n    url = new URL(urlPart);\n  } catch {\n    throw new Error(`Invalid GitHub URL: ${specifier}`);\n  }\n\n  if (url.hostname !== 'github.com') {\n    throw new Error('Only github.com plugin URLs are supported');\n  }\n\n  const [owner, rawRepo, ...rest] = url.pathname.split('/').filter(Boolean);\n  if (!owner || !rawRepo || rest.length > 0) {\n    throw new Error('GitHub plugin URL must be in the form https://github.com/owner/repo');\n  }\n\n  const repo = rawRepo.replace(/\\.git$/, '');","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/plugins/install.ts#L210-L246","documentation":"parseGithubUrl splits the specifier on '#' to separate the URL from an optional git ref. If the part before '#' is empty (or the whole string is empty), it cannot form a URL, so the SDK throws this error. It is the first validation gate when installing a plugin from a GitHub specifier.","triggerScenarios":"Installing a plugin with an empty or whitespace-only specifier, or a specifier that is just a fragment like '#v1.2' with no URL before the '#'.","commonSituations":"An unset environment variable or config field interpolated into the specifier (e.g. PLUGIN_URL='' yields '#main'); a shell command that dropped the URL argument; a script building the specifier from a variable that was never populated.","solutions":["Pass a full https URL as the specifier, e.g. 'https://github.com/owner/repo' or 'https://github.com/owner/repo#v1.2'.","Check the variable/config value feeding the specifier is non-empty before calling the install API.","If a ref is desired, keep it after the URL: 'https://github.com/owner/repo#ref', never a bare '#ref'."],"exampleFix":"// before\nawait installPlugin(process.env.PLUGIN_URL!); // PLUGIN_URL is unset -> \"\"\n// after\nconst specifier = process.env.PLUGIN_URL;\nif (!specifier) throw new Error('PLUGIN_URL is required');\nawait installPlugin(specifier);","handlingStrategy":"validation","validationCode":"function isValidGithubSpecifier(specifier: string): boolean {\n  const urlPart = specifier.split('#', 1)[0];\n  return typeof urlPart === 'string' && urlPart.trim().length > 0;\n}\nif (!isValidGithubSpecifier(specifier)) throw new Error('GitHub plugin specifier is empty');","typeGuard":null,"tryCatchPattern":"try {\n  await installPlugin(specifier);\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Invalid GitHub URL')) {\n    console.error(`Specifier \"${specifier}\" is empty or malformed; use https://github.com/owner/repo[#ref]`);\n  }\n}","preventionTips":["Always build specifiers as full https URLs, never bare fragments.","Assert specifier is non-empty (and trimmed) before any install call.","Centralize specifier construction in one helper so empty config values fail early."],"tags":["plugin-install","url-validation","github"],"backgroundTag":"invalid-url-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}