Skip to content

Fix/pending race condition#9902

Closed
Chukwuebuka-2003 wants to merge 26 commits intowarpdotdev:masterfrom
Chukwuebuka-2003:fix/pending-race-condition
Closed

Fix/pending race condition#9902
Chukwuebuka-2003 wants to merge 26 commits intowarpdotdev:masterfrom
Chukwuebuka-2003:fix/pending-race-condition

Conversation

@Chukwuebuka-2003
Copy link
Copy Markdown

Description
Fix race condition where the file tree shows an empty tree when opened immediately after cloning a repository.
When a user opens the file tree immediately after cloning a repo, the terminal hasn't triggered detection yet, so the repo is in IndexedRepoState::Pending. The previous code used get_repository() which returns None for Pending state, causing an empty entry to be created. This fix checks repository_state explicitly and preserves the lazy-loaded fallback entry while detection completes.
Linked Issue


CHANGELOG-BUG-FIX: Fixed file tree sidebar showing empty when opened immediately after cloning a repository

Chukwuebuka-2003 and others added 10 commits May 1, 2026 22:57
When a user opens the file tree immediately after cloning a repository,
there's a race condition where the terminal hasn't yet triggered git
detection, so the file tree falls back to lazy-loading instead of
triggering proper repo indexing.

This fix checks if a path is a valid git repository by verifying both
the .git directory exists AND .git/HEAD is a valid file. This avoids
treating stale/invalid .git folders as repos. If valid, we trigger
proper git detection instead of lazy-loading, ensuring the full file
tree gets built even when opened immediately after a fresh clone.

Fixes warpdotdev#9846
When a user opens the file tree immediately after cloning a repository,
there's a race condition where the terminal hasn't yet triggered git
detection, so the file tree falls back to lazy-loading instead of
triggering proper repo indexing.

This fix checks if a path has a .git entry (directory or file for
worktrees/submodules) and triggers detection. The async detection
handles validation properly, including gitfile parsing for worktrees.

If the repo has previously failed to index (e.g., exceeded file limit),
we fallback to lazy-loading to avoid repeatedly retrying failed detection.

Fixes warpdotdev#9846
Properly validate .git files (used by worktrees/submodules) by reading
the gitdir path and verifying the actual git directory has a valid HEAD.
This ensures worktrees and submodules also trigger proper repo detection
instead of falling back to lazy-loading.
Test verifies that when file tree is opened on a directory with a
valid .git entry, proper git detection is triggered instead of
falling back to shallow lazy-loading. This addresses reviewer
feedback about adding test coverage for the new detection path.
The previous test passed unconditionally. Now verifies that README.md
and src/ are visible in the file tree - if lazy-loading occurred instead
of proper detection, these items won't be discoverable and the test fails.
Expands src/ and verifies main.rs is visible. First-level entries like
README.md and src appear in both lazy-loaded and fully-indexed trees,
so they don't prove the fix works. Only nested content like src/main.rs
proves proper repo indexing occurred.
This ensures the file tree is never empty if detection returns None or
fails. Previously we only did lazy-loading for non-git dirs, but now
we always register lazy-loaded path first, then trigger detection on
top - detection will replace with full content if successful.
Security: limit synchronous .git file reads to prevent malformed local
folders from stalling the file tree open. gitfile format is small (~100B),
so 1KB is plenty.
Use std::io::Read with a bounded buffer instead of unbounded std::fs::read.
This prevents malicious/malformed .git files from causing large memory
allocations or UI stalls on the view path.
Root cause: When get_repository returns None for Pending state,
the file tree creates an empty entry instead of preserving the
lazy-loaded fallback content.

Fix: Check repo_state explicitly. If Pending and we have a lazy-loaded
path registered, keep that entry instead of showing empty tree. This
preserves the lazy-loaded tree while detection completes.
@cla-bot cla-bot Bot added the cla-signed label May 2, 2026
@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 2, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 2, 2026

@Chukwuebuka-2003

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes May 2, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR attempts to keep the file tree populated while newly cloned repositories move from lazy-loaded file tree state to full repository indexing, and adds an integration test for the regression.

Concerns

  • The Pending handling still returns None when the path was already pending before this view registered a lazy-loaded fallback, which can recreate the empty-tree race.
  • The new integration test does not force or assert the IndexedRepoState::Pending case, and its nested-file interaction can also pass through the existing lazy-loading path.
  • For faster review, please upload screenshots or a video of the file tree behavior working end to end.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread app/src/code/file_tree/view.rs Outdated
Some(IndexedRepoState::Indexed(state)) => Some(state.entry.clone()),
// Pending - repo is being indexed, use lazy-loaded entry if available
Some(IndexedRepoState::Pending) => {
if self.registered_lazy_loaded_paths.contains(path) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This still empties the tree when the repo is already Pending before this view registers a lazy fallback: index_lazy_loaded_path no-ops for Pending, registered_lazy_loaded_paths remains false, and this branch returns None.

.with_action(|app, _, _| open_file_tree_panel(app)),
)
// With the fix, detection triggers immediately on .git dirs, so the repo
// should NOT be in lazy-loaded state. We verify by clicking on main.rs
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This test can still pass through shallow lazy-loading because expanding src loads nested children; make it force/assert the IndexedRepoState::Pending race instead of relying on main.rs visibility.

Check is_lazy_loaded_path from the model in addition to local registry
to handle the case where detection started before file tree opened.
@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 2, 2026

@Chukwuebuka-2003

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes May 2, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR tries to keep the Project Explorer populated while git repository indexing is pending by preserving lazy-loaded entries and triggering repo detection from the file tree.

Concerns

  • The pending-state path can still replace the root with an empty entry when detection has already set the repo to Pending, because lazy registration is skipped in that state.
  • The new regression test can pass through the existing lazy-loading path and does not reliably prove the pending race is fixed.
  • For faster review, please upload screenshots or a video of the file tree working end to end after opening it immediately after cloning a repository.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

// Fully indexed - use the repo's entry
Some(IndexedRepoState::Indexed(state)) => Some(state.entry.clone()),
// Pending - repo is being indexed, use lazy-loaded entry if available
Some(IndexedRepoState::Pending) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] When the repo is already Pending, index_lazy_loaded_path returns without registering a lazy entry, so this branch still falls through to create_empty_entry and shows an empty tree until full indexing completes; create or preserve a shallow fallback for the pending case.

// which only works if full indexing happened (lazy-loading loads shallow tree).
// Note: This tests the detection path; old lazy-loading would need expand.
.with_step(
new_step_with_default_assertions("Expand src to verify nested content loads")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] Expanding src exercises the lazy-loaded path too, so this test can pass without proving full repo indexing or the pending-race fix; reproduce the pending state or assert behavior that lazy-loading alone cannot satisfy.

Previously index_lazy_loaded_path would skip registration when repo
was already Pending, causing empty tree. Now we force local registration
as fallback to prevent empty tree while detection completes.
When repo is in IndexedRepoState::Pending (detection already running),
the file tree previously showed empty because get_repository returns None
for Pending. Now we:
1. Always register lazy-loaded fallback even if model skips it
2. Preserve the fallback entry while detection completes
3. Removed forced .git detection - let the existing detection flow work
@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 2, 2026

@Chukwuebuka-2003

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes May 2, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR attempts to preserve file-tree contents while repo metadata is pending and adds integration coverage for the clone/open race.

Concerns

  • The pending fallback marks paths as locally registered without building the lazy-loaded tree or registering the watcher, so ordinary standalone directories and pending repos with no existing lazy entry can stay empty and skip future indexing.
  • The new integration test does not force IndexedRepoState::Pending or distinguish full git indexing from the existing shallow lazy-loading path.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

if !is_lazy_loaded {
// Model may skip registration if repo is already Pending.
// Force register a local fallback entry to prevent empty tree.
self.registered_lazy_loaded_paths.insert(path.clone());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This marks the path as registered without calling index_lazy_loaded_path; when there is no existing lazy entry, the model never builds the shallow tree or watcher and future refreshes skip indexing because this set now contains the path.

// which only works if full indexing happened (lazy-loading loads shallow tree).
// Note: This tests the detection path; old lazy-loading would need expand.
.with_step(
new_step_with_default_assertions("Expand src to verify nested content loads")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This test does not reproduce the pending-state race: it never forces IndexedRepoState::Pending, and expanding src then clicking main.rs also succeeds through the existing shallow lazy-loading path.

…watcher

Previously we just marked locally registered without triggering the model
to build the lazy-loaded tree. Now we actually call index_lazy_loaded_path
which builds the tree and registers the watcher, then handle Pending state
as fallback.
@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 2, 2026

@Chukwuebuka-2003

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes May 2, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR updates the file tree refresh path to account for repositories that are still in IndexedRepoState::Pending and adds an integration regression test for opening the file tree after cloning.

Concerns

  • Pending repositories can still overwrite an existing lazy-loaded fallback with an empty root entry, so the race can continue to show an empty tree until full indexing finishes.
  • Lazy-loading errors no longer preserve the previous exceeded-file-limit toast or warning log, which turns real failures into a silent empty tree.
  • The new test can pass through the lazy-loading path because it expands src before clicking main.rs; it does not prove full indexing handled the initial open.
  • For faster review, please upload screenshots or a video of the file tree behavior working end to end.

Verdict

Found: 0 critical, 2 important, 3 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread app/src/code/file_tree/view.rs Outdated
let repo_id = repo_metadata::RepositoryIdentifier::local(path.clone());
let repo_state = RepoMetadataModel::as_ref(ctx).repository_state(&repo_id, ctx);
let is_pending = matches!(repo_state, Some(IndexedRepoState::Pending));
let previously_failed = matches!(repo_state, Some(IndexedRepoState::Failed(_)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 [SUGGESTION] previously_failed is never used, so this will trip unused-variable/clippy checks; either remove it or use it in the retry decision.

Comment thread app/src/code/file_tree/view.rs Outdated
}

// For Pending state or failed indexing, ensure we have a placeholder entry
if is_pending || index_result.is_err() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This treats every index_result error as an empty placeholder without restoring the previous ExceededMaxFileLimit toast or warning log, so lazy-loading failures become a silent empty tree; keep the old error handling before falling back.

Comment thread app/src/code/file_tree/view.rs Outdated
// For Pending state or failed indexing, ensure we have a placeholder entry
if is_pending || index_result.is_err() {
if let Some(root_dir) = self.root_directories.get_mut(path) {
root_dir.entry = Self::create_empty_entry(path);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] Pending repositories should not overwrite the existing root entry with create_empty_entry; index_lazy_loaded_path no-ops for Pending, so this clears the shallow fallback that the later branch is trying to preserve.

// which only works if full indexing happened (lazy-loading loads shallow tree).
// Note: This tests the detection path; old lazy-loading would need expand.
.with_step(
new_step_with_default_assertions("Expand src to verify nested content loads")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 [SUGGESTION] This does not prove full indexing: a lazy-loaded root can show src, and expanding it can load main.rs, so the test can pass without exercising the pending-repo fallback.

- Don't overwrite existing entry when lazy-loading fails
- Restore exceeded-file-limit toast and warning log for failures
- Only create placeholder for Pending if no lazy-loaded fallback exists
@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 2, 2026

@Chukwuebuka-2003

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Although the containing impl blocks were already gated, this adds explicit
#[cfg(any(test, feature = test-util))] attributes to the force_pending_state
methods themselves to ensure they are strictly unavailable in production builds
and to provide better clarity for reviewers.
@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 4, 2026

@Chukwuebuka-2003

I ran into an unexpected error while working on this.

Powered by Oz

@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 4, 2026

@Chukwuebuka-2003

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

oz-for-oss[bot]
oz-for-oss Bot previously requested changes May 4, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR changes file tree rendering for repositories in Pending state by adding a pending lazy fallback tree, then adds an integration regression test for opening the file tree immediately after clone.

Concerns

  • The new integration test forces the repository into Pending permanently, but later tries to click src/main.rs; the pending fallback is only first-level lazy content, and directory loading still bails while get_repository() returns None for Pending, so the nested file is not materialized.

Verdict

Found: 0 critical, 1 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

)
.with_step(
new_step_with_default_assertions("Click main.rs to open it")
.with_click_on_saved_position("file_tree_item:main.rs"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] This click targets main.rs, but the test leaves the repo permanently Pending and the pending fallback is built with depth 1, so expanding src cannot materialize src/main.rs (load_directory_from_model returns while get_repository is None). Assert a top-level fallback item or exercise a real completion path before clicking nested content.

@Chukwuebuka-2003
Copy link
Copy Markdown
Author

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 4, 2026

@Chukwuebuka-2003

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I reviewed this pull request and requested human review from: @moirahuang.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

The pending fallback is only a shallow (first-level) lazy tree. Sub-directory
expansion is not supported while the repository is in Pending state because
directory loading requires a real repository handle which is None for Pending.

Modified the integration test to only verify visibility of top-level items
(README.md and src) while the repo is forced into Pending, which correctly
exercises the fix without attempting unsupported nested materialization.
@oz-for-oss oz-for-oss Bot dismissed stale reviews from themself May 4, 2026 19:21

Oz no longer requests changes for this pull request after the latest automated review.

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR preserves a shallow file-tree fallback while repository indexing is still Pending, avoiding the empty-tree state when the Project Explorer opens immediately after clone. It also adds an integration regression covering the Pending fallback path.

Concerns

  • No blocking correctness or security concerns found in the annotated diff.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss Bot requested a review from moirahuang May 4, 2026 19:22
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

This PR preserves a shallow file-tree fallback while repository indexing is still Pending, avoiding the empty-tree state when the Project Explorer opens immediately after clone. It also adds an integration regression covering the Pending fallback path.

Concerns

  • No blocking correctness or security concerns found in the annotated diff.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@moirahuang moirahuang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate you putting up a PR! I ended up also creating a PR yesterday (#9998) which gets at some of the same Pending logic. @alokedesai has said he'll review that PR today

@Chukwuebuka-2003
Copy link
Copy Markdown
Author

Appreciate you putting up a PR! I ended up also creating a PR yesterday (#9998) which gets at some of the same Pending logic. @alokedesai has said he'll review that PR today

That's cool for real

@moirahuang
Copy link
Copy Markdown
Contributor

Closing this since my fix was merged!

@moirahuang moirahuang closed this May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

File tree sidebar does not load when opened immediately after cloning a repository

2 participants