mastra-ai/mastra · critical

Failed to clone repository: ${gitError instanceof Error ? gi

Error message

Failed to clone repository: ${gitError instanceof Error ? gitError.message : 'Unknown error'}

What it means

When degit fails (including the empty-directory case), cloneTemplate falls back to `git clone <repoUrl> <tempTarget>`. If that git clone also throws, the error is rethrown wrapped as 'Failed to clone repository: <git message>' with the temp dir cleaned up. It is the terminal error meaning both scaffold strategies failed.

Source

Thrown at mastracode/mastra-factory/src/utils/clone.ts:73

      if ((await fs.readdir(tempTarget)).length === 0) {
        throw new Error('degit completed without cloning template files');
      }
    } catch {
      await fs.rm(tempTarget, { recursive: true, force: true });

      try {
        await x('git', ['clone', repoUrl, tempTarget], {
          throwOnError: true,
          nodeOptions: { cwd: process.cwd() },
        });

        const gitDir = path.join(tempTarget, '.git');
        if (await directoryExists(gitDir)) {
          await fs.rm(gitDir, { recursive: true, force: true });
        }
      } catch (gitError) {
        throw new Error(
          `Failed to clone repository: ${gitError instanceof Error ? gitError.message : 'Unknown error'}`,
        );
      }
    }

    if (await pathExists(targetPath)) {
      throw new Error(`Directory ${path.basename(targetPath)} already exists`);
    }
    await fs.rename(tempTarget, targetPath);
  } finally {
    await fs.rm(tempRoot, { recursive: true, force: true });
  }
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Read the wrapped git message for the root cause (auth prompt, 404, DNS) and fix accordingly
  2. Verify the repo URL exists and is accessible: `git ls-remote <repoUrl>`
  3. For private repos, configure credentials (SSH key, token in the URL, or git credential helper)
  4. Ensure git is installed and on PATH (`git --version`)
  5. Check network/proxy settings if the remote is unreachable

Example fix

// before
await x('git', ['clone', 'https://github.com/acme/private-template.git', tempTarget], { throwOnError: true });
// after
await x('git', ['clone', 'https://<TOKEN>@github.com/acme/private-template.git', tempTarget], { throwOnError: true });
Defensive patterns

Strategy: try-catch

Validate before calling

const { code } = await x('git', ['ls-remote', repoUrl], { throwOnError: false });
if (code !== 0) throw new Error(`repo unreachable: ${repoUrl}`);

Type guard

null

Try / catch

try {
  await cloneTemplate(opts);
} catch (err) {
  const msg = (err as Error).message;
  if (msg.startsWith('Failed to clone repository')) {
    console.error('Both degit and git clone failed:', msg);
    // surface auth/network remediation to the user
  } else throw err;
}

Prevention

When it happens

Trigger: The degit path threw (caught by the outer catch), and the fallback `git clone repoUrl tempTarget` also failed — non-zero git exit, missing git binary, unreachable remote, bad URL, or cleanup of tempTarget itself throwing.

Common situations: Private repos without credentials, wrong repoUrl after a repo was renamed/deleted, no network or blocked git://(https) access, git not on PATH, or SSH keys absent when the URL is SSH-based.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/6b15813059647690. Report an issue: GitHub.