Skip to content

[Bug]: Initialize Git gives no confirmation for ~30 s when the folder holds a lot of untracked data (change-totals diff times out) #25558

Closed

Description

@kaovilai

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server (the button feedback part is in apps/web)

Steps to reproduce

  1. Make a folder that is not a git repository and holds a lot of untracked data. About 8 GB across 34 text files of 240 MB each reproduces it on an SSD. Each file must stay under git's 512 MB core.bigFileThreshold, otherwise git treats it as binary and skips counting its lines.
    mkdir demo && cd demo
    python3 -c 'l="x"*79+"\n"; open("seed.txt","w").write(l*3000000)'
    for i in $(seq 1 34); do cp seed.txt part$i.txt; done && rm seed.txt
  2. Add the folder as a project (Add project → Local folder) and start a new thread in it. The thread details panel shows Initialize Git.
  3. Click Initialize Git.

Expected behavior

git init takes tens of milliseconds, so the thread should switch to its repository state (branch name, Commit, Changes) right away. At minimum the button should show a busy state until that happens.

Actual behavior

The UI does not confirm the init for about 30 seconds. After that the branch name, Commit and Changes controls become usable.

  • In the recording below, the button is replaced about a second after the click by dimmed, inert main / Commit / Changes +0 −0 rows, which stay that way for about 30 s.
  • In another run, the button stayed enabled and unchanged for more than 8 s after the click, with no pending or disabled state.
  • In neither run is there a spinner, a toast or any other sign that something is happening.

Server trace for the recorded run (ws.rpc.vcs.init returns almost immediately, then the status refresh blocks):

16:35:24.575  ws.rpc.vcs.init                                45 ms     Success
16:35:24.623  VcsStatusBroadcaster.refreshStatus            31903 ms   Success   (ends 16:35:56.525)
16:35:26.451  GitVcsDriver.readBranchChangeTotals           30023 ms   Failure   (git diff --numstat hit the 30 s default timeout)

The first status update after init is built by readLocalStatus, which also computes the "Changes" row totals through readBranchChangeTotals in apps/server/src/vcs/GitVcsDriverCore.ts. For a repository with no commits, that runs git diff --numstat against the empty tree with every untracked file added to a temporary index. Git reads each file to count lines. REVIEW_UNTRACKED_MAX_FILES limits the file count but not the total size. The call has no timeout of its own, so it waits for the 30 s default; the failure is then swallowed (Effect.orElseSucceed(() => undefined)), and only after that does the isRepo: true status reach the client.

The same totals are recomputed on every status refresh. In the traces of my regular install (a large, mostly untracked folder), each 30 s status poll hit the same 30 s timeout.

Impact

Minor bug or occasional failure

Version or commit

main @ 3f94487 (dev server, vp run dev, isolated state)

Environment

macOS 26 (Darwin 25.6), T3 Code web client in the in-app Browser panel, git 2.53

Logs or stack traces

See the spans above.

Screenshots, recordings, or supporting files

Recording of the run above (about 40 s):

c9579205-50b9-4791-ac75-41a9affb3407-414c6e10-e57c-43bc-8a52-129668c23813-mp4.mp4

Workaround

None in the app. A short wait, or restarting the server, brings the repository state up.

Related

I have a small fix ready (a short timeout on the totals diff, so the row falls back to the uncommitted totals, plus keeping the button busy until the status reports the new repository). I'll wait for triage before opening a PR.

Note

Responses generated with Claude (model: Claude Sonnet 5.5)

Activity

  1. luis-7654 commented on Nov 7, 2026

    @luis-7654

    Additional reproduction for the same unbounded untracked-input path: a single sparse file can stall Changes totals, even in a tiny repository.

    Observed in a Linux self-hosted T3 0.1.5 environment: an untracked crash dump had 1,378,833,166,336 bytes of logical size, but only 9,240,064 bytes allocated. The active git diff --numstat -z <base> -- process used T3's temporary review index, held that file open and consumed approximately one CPU core. The remaining tracked repository was about 5.45 MB. Removing the dump and excluding future dump files stopped the scan; a direct regular-index diff then completed in 0.0078 s. This last measurement is not a T3 UI benchmark or the exact temporary-index path.

    The same missing preflight remains in current main 4c3a9d58184f20bd76833bd599c3544d2c09e3eb: prepareUntrackedReviewIndex caps file count but passes all candidate paths to prepareReviewIndex, which adds intent-to-add entries before readBranchChangeTotals reads numstat. Output caps cannot prevent reading a huge input. The selected-file 1 MB expansion guard runs later and does not protect this path.

    A synthetic Git-level reproduction in a disposable container (no network, one CPU, 256 MiB RAM, read-only root, 16 MiB tmpfs) used one 8 GiB sparse file with only 4 KiB allocated. A fresh temporary index with git add --intent-to-add followed by git diff --numstat exceeded a two-second subprocess limit. A preflight prototype using logical file size and excluding the oversized untracked input before adding it to the index completed the diff in about 3.5 ms. This is a mechanism reproduction, not a patched T3 integration test.

    Suggested scope: bound per-file and aggregate untracked input bytes before content-reading Git commands, preserve normal file review, and explicitly report incomplete/skipped large-file statistics rather than presenting partial totals as exact. Reuse the policy for Changes totals and review previews. Include a focused regression using a sparse fixture so tests do not allocate huge files. Timeout remains a fallback; filename-only crash-dump exclusions would not cover other oversized files.

    No crash dump contents, credentials or private workspace paths are included. The suspected browser crash that created the dump is a separate, unconfirmed cause and need not be part of this bug fix.

    Implementation submitted in #32463: 10 MiB per untracked file, 64 MiB in total, with incomplete previews and omitted unavailable branch totals. Both focused regression tests fail on the base commit and pass with the patch. vp run check:changed --concurrency 1 passed all steps, including 242 tests across two files. Upstream CI and maintainer review remain separate.

    Prepared with GPT-6.1 Sol in the Codex harness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions