{"record":{"id":"85ca8810fb2f6b9f","repo":"gitbutlerapp/gitbutler","slug":"could-not-find-selected-project-usepathmenuitems","errorCode":null,"errorMessage":"Could not find selected project","messagePattern":"Could not find selected project","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/lite/ui/src/routes/project/$id/workspace/usePathMenuItems.ts","lineNumber":43,"sourceCode":"\tpath: string;\n\t/** The linked worktree the file lives in; the project's own checkout when unset. */\n\tworktree?: string;\n}): Array<NativeMenuItem> => {\n\tconst { data: projects } = useSuspenseQuery(listProjectsQueryOptions);\n\tconst { data: editors } = useQuery(listEditorsQueryOptions);\n\tconst { data: preferredEditor } = useQuery({\n\t\t...guiSettingsQueryOptions,\n\t\tselect: (cfg) => editors?.find((editor) => editor.id === cfg.editorId),\n\t});\n\tconst { data: worktreePath } = useQuery({\n\t\t...worktreesListQueryOptions(projectId),\n\t\tenabled: worktree !== undefined,\n\t\tselect: (listing) =>\n\t\t\t[...listing.active, ...listing.archived].find((entry) => entry.name === worktree)?.path,\n\t});\n\n\tconst selectedProject = projects.find((project) => project.id === projectId);\n\tif (!selectedProject) throw new Error(\"Could not find selected project\");\n\t// The checkout the path is relative to; unknown until the worktree listing answers.\n\tconst basePath = worktree === undefined ? selectedProject.path : worktreePath;\n\tconst absolutePath = () => window.lite.pathJoin(basePath ?? \"\", path);\n\n\tconst { isPending: isOpenInProgramPending, mutate: openInProgram } = useOpenInProgram();\n\t// The backend resolves the path against the project's checkout, so a linked\n\t// worktree's file is handed over absolute, which joining leaves untouched.\n\tconst openPath = async (programId: string) => {\n\t\tconst target = worktree === undefined ? path : await absolutePath();\n\t\topenInProgram({ projectId, programId, path: target, lineNr: null });\n\t};\n\tconst canOpen = !isOpenInProgramPending && basePath !== undefined;\n\n\treturn [\n\t\tpreferredEditor\n\t\t\t? nativeMenuItem({\n\t\t\t\t\tlabel: `Open in ${preferredEditor.name}`,\n\t\t\t\t\tenabled: canOpen,","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/apps/lite/ui/src/routes/project/$id/workspace/usePathMenuItems.ts#L25-L61","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst selectedProject = projects.find((project) => project.id === projectId);\nif (!selectedProject) throw new Error(\"Could not find selected project\");\n\n// after\nconst { data: projects, isPending } = useQuery(listProjectsQueryOptions);\nconst selectedProject = projects?.find((project) => project.id === projectId);\nif (!isPending && !selectedProject) {\n  return null; // let the route redirect instead of throwing during render\n}","handlingStrategy":"validation","validationCode":"const projects = useQuery(listProjectsQueryOptions).data ?? [];\nconst selectedProject = projects.find((p) => p.id === projectId);\nif (!selectedProject) return null; // or redirect before calling the hook's consumers","typeGuard":"function findProject(projects: Project[] | undefined, id: string): Project | undefined {\n  return projects?.find((p) => p.id === id);\n}","tryCatchPattern":"try {\n  const items = usePathMenuItemsResult;\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"Could not find selected project\")) {\n    showEmptyState(\"Project unavailable\");\n  } else throw e;\n}","preventionTips":["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."],"tags":["react","state","frontend"],"backgroundTag":"entity-not-found","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}