can1357/oh-my-pi · error · Error

Not a git repository

Error message

Not a git repository

What it means

GitTuiState.refresh polls repository status via statusPorcelain; if that call fails (caught to null) it treats the directory as no longer being a valid git repository and throws. This guards the state machine against operating on a repo that disappeared mid-session (e.g. .git removed) — the TUI data can no longer be trusted.

Source

Thrown at packages/coding-agent/src/cli/git-tui/state.ts:336

	get clean(): boolean {
		return this.unstaged.length === 0 && this.staged.length === 0;
	}

	/** Re-read fast repository state; expensive numstats load separately. */
	async refresh(): Promise<boolean> {
		if (this.pinnedSha) {
			if (this.#fingerprint === this.pinnedSha) return false;
			this.#fingerprint = this.pinnedSha;
			this.#headFilesLoad = null;
			this.headCommit = await this.#loadHeadMetadata(this.pinnedSha);
			return true;
		}
		const [statusText, branchName, headSha] = await Promise.all([
			this.#repo.statusPorcelain({ nulTerminated: true, untracked: "all" }).catch(() => null),
			this.#repo.currentBranch(),
			this.#repo.headSha(),
		]);
		if (statusText === null) throw new Error("Not a git repository");
		const fingerprint = `${headSha ?? ""}\u0000${statusText}`;
		if (fingerprint === this.#fingerprint) {
			this.branch = branchName ?? null;
			return false;
		}
		this.#fingerprint = fingerprint;
		this.#statusStatsLoad = null;
		this.branch = branchName ?? null;
		this.#setChanges(statusText);
		if ((headSha ?? null) !== this.headCommit?.sha) {
			this.#headFilesLoad = null;
			this.headCommit = headSha ? await this.#loadHeadMetadata(headSha) : null;
		}
		return true;
	}

	/** Populate changed-line counts after the file list is already interactive. */
	async loadChangeStats(): Promise<boolean> {

View on GitHub (pinned to 9690622007)

Solutions

  1. Close and reopen the git TUI from a valid repository.
  2. Restore the .git directory (undo deletion) or re-clone the repository.
  3. Run `git status` manually to confirm the repository is healthy.

Example fix

// before
state.refresh(); // throws if repo vanished
// after
try { await state.refresh(); } catch { closeGitTui(); }
Defensive patterns

Strategy: try-catch

Validate before calling

import { $ } from "bun";
const ok = await $`git -C ${repoRoot} status --porcelain`.quiet().nothrow();
if (ok.exitCode !== 0) teardownGitTui(); // repo is gone/corrupt

Try / catch

try {
  await state.refresh();
} catch (e) {
  if (e instanceof Error && e.message === "Not a git repository") {
    ui.hide(gitOverlay); // repo vanished mid-session
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling refresh (directly or via create/loadStagedContents) on a state whose repo's statusPorcelain fails — .git directory deleted while the TUI is open, repo corrupted, or git binary failing.

Common situations: Deleting or moving the .git directory while the git TUI is open, checking out into a broken worktree, or disk/filesystem errors making git status fail.

Related errors


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