{"record":{"id":"7401b7019503796d","repo":"mastra-ai/mastra","slug":"push-failed","errorCode":"push-failed","errorMessage":"Refusing to push: invalid repo full name '${repoFullName}'.","messagePattern":"Refusing to push: invalid repo full name '(.+?)'\\.","errorType":"exception","errorClass":"MaterializeError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/github/sandbox.ts","lineNumber":688,"sourceCode":" * **always** scrub the remote back to the tokenless URL afterwards. The token\n * therefore only ever lives in the remote URL for the duration of the\n * operation and is never left in the VM's git config.\n *\n * Once the tokenized URL is installed a failed scrub may leave the token\n * persisted, so it is always surfaced: on its own after a successful `fn`,\n * appended to `fn`'s own error otherwise — `fn`'s error is never replaced.\n * Only a failed set-url (the token never reached the remote) downgrades the\n * scrub to best-effort.\n */\nexport async function withInstallToken<T>(\n  sandbox: ExecutableSandbox,\n  workdir: string,\n  repoFullName: string,\n  token: string,\n  fn: () => Promise<T>,\n): Promise<T> {\n  if (!/^[\\w.-]+\\/[\\w.-]+$/.test(repoFullName)) {\n    throw new MaterializeError(`Refusing to push: invalid repo full name '${repoFullName}'.`, 'push-failed');\n  }\n\n  const setUrl = await sh(\n    sandbox,\n    `git -C ${shellQuote(workdir)} remote set-url origin ${shellQuote(tokenUrl(repoFullName, token))}`,\n  );\n  if (setUrl.exitCode !== 0) {\n    // Best-effort scrub even though set-url failed, then surface the failure.\n    await scrubRemote(sandbox, workdir, repoFullName, false);\n    throw new MaterializeError(`Failed to set git remote: ${setUrl.stderr.trim()}`, 'push-failed');\n  }\n\n  let result: T;\n  try {\n    result = await fn();\n  } catch (primary) {\n    throw await scrubbedFailure(sandbox, workdir, repoFullName, true, primary, 'push-failed');\n  }","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/github/sandbox.ts#L670-L706","documentation":"withInstallToken validates the repo full name against /^[\\w.-]+\\/[\\w.-]+$/ before rewriting `origin` to a tokenized URL, throwing MaterializeError('push-failed') on mismatch. This guard prevents embedding an untrusted string into a remote URL (and later into a shell command), which could leak the install token or execute injection.","triggerScenarios":"Calling pushBranch with a repoFullName that is empty, contains spaces/slashes beyond the single owner/name separator, or shell metacharacters — e.g. a malformed value parsed from a git remote or an API payload.","commonSituations":"Parsing owner/name from a URL incorrectly (leaving 'https://github.com/' prefix), user-supplied repo names with typos, or passing a full clone URL instead of 'owner/repo'.","solutions":["Normalize the repo full name to 'owner/repo' form before calling pushBranch","Strip protocol/host if you have a full URL (extract the last two path segments)","Trim whitespace and reject empty strings at the input boundary"],"exampleFix":"// before\nawait pushBranch(sandbox, workdir, 'https://github.com/acme/widgets.git', branch, token);\n// after\nconst repoFullName = 'acme/widgets';\nawait pushBranch(sandbox, workdir, repoFullName, branch, token);","handlingStrategy":"validation","validationCode":"if (!/^[\\w.-]+\\/[\\w.-]+$/.test(repoFullName)) {\n  throw new Error(`invalid repo full name: ${repoFullName}`);\n}","typeGuard":"function isValidRepoFullName(name: string): name is `${string}/${string}` {\n  return /^[\\w.-]+\\/[\\w.-]+$/.test(name);\n}","tryCatchPattern":"try {\n  await pushBranch(sandbox, workdir, branch, token, repoFullName);\n} catch (e) {\n  if (e instanceof MaterializeError && e.code === 'push-failed' && e.message.includes('invalid repo full name')) {\n    repoFullName = normalizeRepoFullName(repoFullName);\n    return pushBranch(sandbox, workdir, branch, token, repoFullName);\n  }\n  throw e;\n}","preventionTips":["Store owner/repo as two separate fields and join at call time","Extract repo name from URLs with a strict parser","Never pass full clone URLs as repoFullName"],"tags":["git","validation","security"],"backgroundTag":"invalid-git-repo-name","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}