gitbutlerapp/gitbutler · error
Could not find selected project
Error message
Could not find selected project
What it means
useHunkMenuItems looks up the project matching the projectId route param from the cached projects list and throws synchronously when none matches. This is an internal invariant: the hook's menu-item construction cannot proceed without a valid project. It guards against building hunk actions (commit/discard/uncommit) against a nonexistent project.
Solutions
- Verify the projectId route param matches an existing project id in the projects list (log both to compare).
- Gate the hook/route behind the list-projects query's isLoading state so it only runs once projects are loaded.
- If the project was deleted, navigate the user back to the project list instead of rendering the workspace route.
- Refresh the projects list (invalidate listProjectsQueryOptions) if another client may have changed it.
Example fix
// before
const selectedProject = projects.find((project) => project.id === projectId);
if (!selectedProject) throw new Error("Could not find selected project");
// after
const selectedProject = projects.find((project) => project.id === projectId);
useEffect(() => {
if (!selectedProject && !isProjectsLoading) {
navigate({ to: "/" }); // leave the stale project route
}
}, [selectedProject, isProjectsLoading]); Defensive patterns
Strategy: validation
Validate before calling
const projects = useQuery(listProjectsQueryOptions).data ?? []; const canRender = projects.some((p) => p.id === projectId);
Type guard
const hasProject = (projects: Project[], id: string): projects is [Project, ...Project[]] & Project[] => projects.some((p) => p.id === id);
Try / catch
try {
const items = buildHunkMenuItems(projectId, ...);
} catch (e) {
if (e instanceof Error && e.message === "Could not find selected project") {
navigate({ to: "/" });
} else throw e;
} Prevention
- Gate workspace routes on the projects query's success state.
- Redirect away from routes whose project id no longer exists.
- Invalidate the projects query after mutations that add/remove projects.
When it happens
Trigger: Calling useHunkMenuItems(projectId) while the projects query cache does not contain a project whose id equals projectId — e.g. the project was removed, the list query hasn't resolved yet, or the route id is stale/invalid.
Common situations: A project is deleted in another window/instance while a workspace route for it is open; deep-linking to /project/<bad-id>/workspace; the projects list query failing or still loading so the cache is empty; using a stale bookmarked id after re-cloning.
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/aa4272384a213bb6.
Report an issue: GitHub.
Appendix: source
Thrown at apps/lite/ui/src/routes/project/$id/workspace/useHunkMenuItems.ts:54
checkedProbe: Extract<Address, { _tag: "Hunk" }> | null;
usesSelectedLines: boolean;
};
export const useHunkMenuItems = ({
projectId,
}: {
projectId: string;
}): ((target: HunkMenuTarget) => Array<NativeMenuItem>) => {
const store = useAppStore();
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 selectedProject = projects.find((project) => project.id === projectId);
if (!selectedProject) throw new Error("Could not find selected project");
const { isPending: isCommitUncommitChangesPending, mutate: commitUncommitChanges } =
useCommitUncommitChanges();
const { isPending: isCommitDiscardChangesPending, mutate: commitDiscardChanges } =
useCommitDiscardChanges();
const { isPending: isDiscardWorktreeChangesPending, mutate: discardWorktreeChanges } =
useDiscardWorktreeChanges();
const { isPending: isOpenInProgramPending, mutate: openInProgram } = useOpenInProgram();
const revealInFolder = useRevealInFolder(projectId);
return ({ sources, checkedProbe, usesSelectedLines, change, hunk, lineNumber }) => {
const state = store.getState();
const usesCheckedLines =
checkedProbe !== null &&
projectSlice.selectors.selectAddressChecked(state, projectId, checkedProbe);
const cutSources = usesCheckedLines
? projectSlice.selectors.selectCheckedAddresses(state, projectId)
: sources;View on GitHub (pinned to 58e5313667)