can1357/oh-my-pi · error · Error

Security scans require a Git repository: ${request.cwd}

Error message

Security scans require a Git repository: ${request.cwd}

What it means

buildPlanMaterial resolves the Git repository root for a security scan plan via the git adapter. If `adapter.root()` returns null (the resolved path is not inside a Git work tree), the function throws to refuse planning a scan outside version control. Security scans are anchored to a repo root so findings are repository-relative and reproducible.

Source

Thrown at packages/coding-agent/src/security/preflight.ts:320

}

interface SecurityPlanMaterial {
	repositoryRoot: string;
	target: SecurityTarget;
	knowledgeBases: SecurityKnowledgeBaseRef[];
	output: SecurityOutputPlan;
	model: SecurityModelRef;
	account: SecurityAccountRef;
	configFingerprint: string;
	workflowFingerprint: string;
}

async function buildPlanMaterial(
	request: SecurityPlanRequest,
	adapter: SecurityGitAdapter,
): Promise<SecurityPlanMaterial> {
	const repositoryRoot = await adapter.root(path.resolve(request.cwd), request.signal);
	if (!repositoryRoot) throw new Error(`Security scans require a Git repository: ${request.cwd}`);
	const canonicalRoot = await fs.realpath(repositoryRoot);
	const target = await normalizeTarget(canonicalRoot, request.target, adapter, request.signal);
	const knowledgeBases = await normalizeKnowledgeBases(request.knowledgeBasePaths, canonicalRoot);
	const output = await normalizeOutput(canonicalRoot, request.outputRoot, request.archiveExisting ?? false);
	const model: SecurityModelRef = {
		provider: request.model.provider,
		modelId: request.model.modelId,
	};
	if (request.model.thinkingLevel !== undefined) model.thinkingLevel = request.model.thinkingLevel;
	const account: SecurityAccountRef = {
		provider: request.account.provider,
		credentialId: request.account.credentialId,
	};
	if (request.account.accountId !== undefined) account.accountId = request.account.accountId;
	if (request.account.email !== undefined) account.email = request.account.email;
	if (request.account.organizationId !== undefined) account.organizationId = request.account.organizationId;
	if (request.account.organizationName !== undefined) account.organizationName = request.account.organizationName;
	return {

View on GitHub (pinned to 9690622007)

Solutions

  1. Run `git init` (and an initial commit) in request.cwd or a parent directory so a work-tree root resolves
  2. Pass a cwd that is inside an existing Git repository
  3. Verify with `git -C <cwd> rev-parse --show-toplevel` that the root resolves before invoking the scan
  4. If .git was lost during copy/extract, restore it or re-clone the project

Example fix

// before
await material({ cwd: "/tmp/snapshot", ... });
// after
const $ = Bun.$; await $`git -C /tmp/snapshot rev-parse --show-toplevel`; // ensure repo first
await material({ cwd: "/home/me/project", ... });
Defensive patterns

Strategy: validation

Validate before calling

const root = await $`git -C ${cwd} rev-parse --show-toplevel`.nothrow();
if (root.exitCode !== 0) throw new Error(`cwd is not a git repo: ${cwd}`);

Type guard

null

Try / catch

try { await material(request); } catch (e) { if (String(e.message).includes("require a Git repository")) { /* git init or fix cwd */ } else throw e; }

Prevention

When it happens

Trigger: Calling material()/buildPlanMaterial() with a request.cwd that is outside any Git repository, inside a bare repo where root() cannot resolve a work tree, or in a directory whose .git was removed/corrupted.

Common situations: Running security scans on a downloaded source snapshot or extracted tarball, pointing the scan at a temp directory, initializing a project without `git init`, or .git being excluded when copying a project.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/04255e3497f74983. Report an issue: GitHub.