gitbutlerapp/gitbutler · error
Could not find selected project
Error message
Could not find selected project
What it means
usePathMenuItems resolves the project for the given projectId from the cached projects list and throws when no project matches. The hook needs the project's checkout path (or a worktree path) to build absolute paths for menu items like open-in-program, so a missing project is unrecoverable.
Solutions
- Confirm the projectId param exists in the cached projects list (log the list vs the id).
- Wait for the list-projects query to finish loading before rendering the component that calls the hook.
- Redirect to the project list when the id is no longer valid after a project deletion.
- Invalidate/refetch the projects query if project data may be stale after external changes.
Example fix
// before
const selectedProject = projects.find((project) => project.id === projectId);
if (!selectedProject) throw new Error("Could not find selected project");
// after
const { data: projects, isPending } = useQuery(listProjectsQueryOptions);
const selectedProject = projects?.find((project) => project.id === projectId);
if (!isPending && !selectedProject) {
return null; // let the route redirect instead of throwing during render
} Defensive patterns
Strategy: validation
Validate before calling
const projects = useQuery(listProjectsQueryOptions).data ?? []; const selectedProject = projects.find((p) => p.id === projectId); if (!selectedProject) return null; // or redirect before calling the hook's consumers
Type guard
function findProject(projects: Project[] | undefined, id: string): Project | undefined {
return projects?.find((p) => p.id === id);
} Try / catch
try {
const items = usePathMenuItemsResult;
} catch (e) {
if (e instanceof Error && e.message.includes("Could not find selected project")) {
showEmptyState("Project unavailable");
} else throw e;
} Prevention
- Only render path-dependent menus once the projects list has loaded.
- Treat a missing project id as a routing problem and redirect, don't throw during render.
- Keep project deletion in sync: invalidate queries and navigate away from deleted project routes.
When it happens
Trigger: Invoking usePathMenuItems(projectId, ...) when projects.find((p) => p.id === projectId) is undefined — the projects query cache is empty/stale, the project was deleted elsewhere, or the route id doesn't correspond to any listed project.
Common situations: Opening a file's context menu in a workspace whose project was removed in another window; landing on a stale /project/<id> deep link; projects list still loading on app startup so the cache lookup fails before data arrives.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Could not find selected project
- Could not find selected project
- Can't receive a change without it's source or commit
- Change drop data must specify the source stackId
- Commit is not in the applied workspace
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/85ca8810fb2f6b9f.
Report an issue: GitHub.
Appendix: source
Thrown at apps/lite/ui/src/routes/project/$id/workspace/usePathMenuItems.ts:43
path: string;
/** The linked worktree the file lives in; the project's own checkout when unset. */
worktree?: string;
}): Array<NativeMenuItem> => {
const { data: projects } = useSuspenseQuery(listProjectsQueryOptions);
const { data: editors } = useQuery(listEditorsQueryOptions);
const { data: preferredEditor } = useQuery({
...guiSettingsQueryOptions,
select: (cfg) => editors?.find((editor) => editor.id === cfg.editorId),
});
const { data: worktreePath } = useQuery({
...worktreesListQueryOptions(projectId),
enabled: worktree !== undefined,
select: (listing) =>
[...listing.active, ...listing.archived].find((entry) => entry.name === worktree)?.path,
});
const selectedProject = projects.find((project) => project.id === projectId);
if (!selectedProject) throw new Error("Could not find selected project");
// The checkout the path is relative to; unknown until the worktree listing answers.
const basePath = worktree === undefined ? selectedProject.path : worktreePath;
const absolutePath = () => window.lite.pathJoin(basePath ?? "", path);
const { isPending: isOpenInProgramPending, mutate: openInProgram } = useOpenInProgram();
// The backend resolves the path against the project's checkout, so a linked
// worktree's file is handed over absolute, which joining leaves untouched.
const openPath = async (programId: string) => {
const target = worktree === undefined ? path : await absolutePath();
openInProgram({ projectId, programId, path: target, lineNr: null });
};
const canOpen = !isOpenInProgramPending && basePath !== undefined;
return [
preferredEditor
? nativeMenuItem({
label: `Open in ${preferredEditor.name}`,
enabled: canOpen,View on GitHub (pinned to 58e5313667)