{"record":{"id":"79822d77f035a110","repo":"stablyai/orca","slug":"access-denied-unknown-repository","errorCode":null,"errorMessage":"Access denied: unknown repository","messagePattern":"Access denied: unknown repository","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/hosted-review.ts","lineNumber":27,"sourceCode":"import type { Repo } from '../../shared/types'\nimport type { Store } from '../persistence'\nimport type { StatsCollector } from '../stats/collector'\nimport {\n  createHostedReview,\n  getHostedReviewCreationEligibility\n} from '../source-control/hosted-review-creation'\nimport { createStackedHostedReview } from '../source-control/stacked-hosted-review-creation'\nimport { getHostedReviewForBranch } from '../source-control/hosted-review'\nimport { resolveRegisteredWorktreePath } from './filesystem-auth'\nimport { listRepoWorktrees } from '../repo-worktrees'\nimport { getLocalProjectWorktreeGitOptions } from '../project-runtime-git-options'\nimport { getWorktreeSharedLinkPaths } from '../git/worktree-shared-directories'\n\nfunction assertRegisteredRepo(repoPath: string, store: Store, repoId?: string): Repo {\n  if (repoId) {\n    const repo = store.getRepo(repoId)\n    if (!repo || repo.path !== repoPath) {\n      throw new Error('Access denied: unknown repository')\n    }\n    return repo\n  }\n  const resolvedRepoPath = resolve(repoPath)\n  const repo = store.getRepos().find((r) => resolve(r.path) === resolvedRepoPath)\n  if (!repo) {\n    throw new Error('Access denied: unknown repository path')\n  }\n  return repo\n}\n\nasync function resolveHostedReviewWorktreePath(\n  repo: Repo,\n  store: Store,\n  worktreePath?: string\n): Promise<string> {\n  if (!worktreePath) {\n    return repo.path","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/hosted-review.ts#L9-L45","documentation":"Thrown by assertRegisteredRepo in hosted-review.ts:21 on the repoId branch: store.getRepo(repoId) returns no repo, OR the returned repo's path does not equal the supplied repoPath. This guards hosted-review (PR) handlers so a repoId that is unknown or whose path disagrees with the request is rejected as 'Access denied: unknown repository'.","triggerScenarios":"A hosted-review IPC handler calls assertRegisteredRepo(repoPath, store, repoId) with a repoId. Denied when store.getRepo(repoId) is falsy, or when the found repo's repo.path !== repoPath. The id/path pair is inconsistent.","commonSituations":"Stale repoId persisted after the repo was removed and re-added (new id); repoPath drifted (moved/symlink) while repoId stayed; cross-host replay with a repoId from a different store; manually edited/corrupted repoId.","solutions":["Re-resolve the repoId from the current store for the given path before calling (e.g. look up by path), then retry.","If the repo was re-added, refresh persisted references to the new repoId.","Drop the repoId and let the path-based lookup (hosted-review.ts:31) handle it when the id is untrusted.","Validate id/path consistency at the call site before dispatching."],"exampleFix":"// before\nassertRegisteredRepo(repoPath, store, persistedRepoId)\n\n// after — reconcile id against path before asserting\nconst byPath = store.getRepos().find((r) => resolve(r.path) === resolve(repoPath))\nif (!byPath) throw new Error('repo not registered')\nassertRegisteredRepo(repoPath, store, byPath.id)","handlingStrategy":"validation","validationCode":"// Before a hosted-review IPC call keyed by repoId: reconcile id with path\nimport { resolve } from 'node:path'\n\nfunction reconcileRepoId(store, repoPath, repoId) {\n  if (!repoId) return store.getRepos().find((r) => resolve(r.path) === resolve(repoPath))?.id\n  const repo = store.getRepo(repoId)\n  return repo && resolve(repo.path) === resolve(repoPath) ? repoId : undefined\n}","typeGuard":"export function isHostedReviewRepoRef(\n  args: unknown\n): args is { repoPath: string; repoId?: string } {\n  return typeof args === 'object' && args !== null && typeof (args as any).repoPath === 'string'\n}","tryCatchPattern":"try {\n  await ipcRenderer.invoke('hostedReview:something', { repoPath, repoId })\n} catch (e) {\n  if (e instanceof Error && e.message === 'Access denied: unknown repository') {\n    const freshId = reconcileRepoId(store, repoPath, repoId); promptRefreshRepoId(freshId); return\n  }\n  throw e\n}","preventionTips":["Re-resolve repoId from the current store by path before reusing a persisted id.","After removing and re-adding a repo, refresh all persisted repoId references.","Drop an untrusted repoId and fall back to the path-only lookup.","Validate id/path consistency at the call site before dispatching."],"tags":["security","authorization","hosted-review","repo-registration","filesystem-auth"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}