mastra-ai/mastra · error · MaterializeError

commit-failed

commit-failed

Error message

Failed to set git user.name: ${setName.stderr.trim()}

What it means

configureGitIdentity sets `git config user.name` (and then user.email) in the session workdir before commits; a non-zero exit from the user.name command throws this MaterializeError with code commit-failed and the trimmed git stderr. Without an identity, git commits would fail, so the library aborts the commit flow early.

Solutions

  1. Inspect the trimmed stderr in the message for the concrete git error.
  2. Confirm the workdir still contains a valid .git directory; re-materialize if it was wiped.
  3. Free disk space or fix permissions if the error is ENOSPC/EACCES.
  4. Simplify the GitIdentity name/email to plain ASCII values and retry commitAll.

Example fix

// before
identity = { name: 'Agent O\'Brien \u2013 bot', email: 'not-an-email' };
// after
identity = { name: 'mastra-agent', email: 'agent@example.com' };
Defensive patterns

Strategy: try-catch

Validate before calling

const { name, email } = resolveGitIdentity(identity);
if (!/^[^\n\r;|&$`<>]+$/.test(name) || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
  throw new Error('GitIdentity name/email must be simple, valid values');
}

Type guard

const isPlainIdentity = (i: GitIdentity): boolean => {
  const { name, email } = resolveGitIdentity(i);
  return name.length > 0 && !/[\n\r]/.test(name) && /^[^@\s]+@[^@\s]+$/.test(email);
};

Try / catch

try {
  await commitAll(sandbox, workdir, identity);
} catch (err) {
  if (err instanceof MaterializeError && err.code === 'commit-failed' && err.message.includes('user.name')) {
    throw new Error(`git config user.name failed in sandbox: ${err.message}`, { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: `git -C <workdir> config user.name <name>` failing because the workdir isn't a git repository (.git missing), the filesystem is read-only/full, the shell-quoted identity contains characters git rejects, or git is missing/broken in the sandbox.

Common situations: Sandbox disk quota exceeded; .git removed by session activity; custom GitIdentity values that upset git config parsing; sandbox image without git (usually caught earlier by the git-missing preflight).

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


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

Appendix: source

Thrown at mastracode/factory/src/integrations/github/sandbox.ts:660

  const email =
    (identity.email || '').trim() ||
    (login ? `${login}@users.noreply.github.com` : 'mastra-code@users.noreply.github.com');
  return { name, email };
}

/**
 * Configure `user.name` / `user.email` for the given repo working tree inside
 * the sandbox so commits are authored correctly. Values are shell-quoted.
 */
export async function configureGitIdentity(
  sandbox: ExecutableSandbox,
  workdir: string,
  identity: GitIdentity,
): Promise<void> {
  const { name, email } = resolveGitIdentity(identity);
  const setName = await sh(sandbox, `git -C ${shellQuote(workdir)} config user.name ${shellQuote(name)}`);
  if (setName.exitCode !== 0) {
    throw new MaterializeError(`Failed to set git user.name: ${setName.stderr.trim()}`, 'commit-failed');
  }
  const setEmail = await sh(sandbox, `git -C ${shellQuote(workdir)} config user.email ${shellQuote(email)}`);
  if (setEmail.exitCode !== 0) {
    throw new MaterializeError(`Failed to set git user.email: ${setEmail.stderr.trim()}`, 'commit-failed');
  }
}

/**
 * Temporarily rewrite `origin` to a tokenized URL, run `fn` (e.g. a push), and
 * **always** scrub the remote back to the tokenless URL afterwards. The token
 * therefore only ever lives in the remote URL for the duration of the
 * operation and is never left in the VM's git config.
 *
 * Once the tokenized URL is installed a failed scrub may leave the token
 * persisted, so it is always surfaced: on its own after a successful `fn`,
 * appended to `fn`'s own error otherwise — `fn`'s error is never replaced.
 * Only a failed set-url (the token never reached the remote) downgrades the
 * scrub to best-effort.

View on GitHub (pinned to 75dd419e61)