{"record":{"id":"72d8ef4bcef27084","repo":"mastra-ai/mastra","slug":"repository-link-session-projectrepositoryid-is","errorCode":null,"errorMessage":"Repository link ${session.projectRepositoryId} is incomplete","messagePattern":"Repository link (.+?) is incomplete","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/workspace.ts","lineNumber":295,"sourceCode":"    }\n    if (!sandboxConfig || !github) {\n      throw new Error('GitHub and a sandbox callback are required to create a Factory session workspace');\n    }\n    const createSessionSandboxInstance = sandboxConfig;\n\n    const storage = github.sourceControlStorage;\n    const projectRepository = await storage.projectRepositories.get({\n      orgId: session.orgId,\n      id: session.projectRepositoryId,\n    });\n    if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);\n    // The remaining reads only depend on the repository link — issue them in\n    // parallel instead of paying four sequential storage round-trips.\n    const [connection, repository] = await Promise.all([\n      storage.connections.get({ orgId: session.orgId, id: projectRepository.connectionId }),\n      storage.repositories.get({ orgId: session.orgId, id: projectRepository.repositoryId }),\n    ]);\n    if (!connection || !repository) throw new Error(`Repository link ${session.projectRepositoryId} is incomplete`);\n    const installation = await storage.installations.get({ orgId: session.orgId, id: connection.installationId });\n    if (!installation) throw new Error(`GitHub installation ${connection.installationId} was not found`);\n    const repoFullName = repository.slug;\n\n    // Construct (or fetch) the session's memoized sandbox instance.\n    // Construction is cheap and side-effect-free by the callback contract —\n    // the VM is provisioned on `start()`, which only the materialization\n    // pipeline calls. The workdir is never persisted or trusted from storage\n    // or client input (the stale-workdir incident class came from reusing\n    // `session.sandboxWorkdir` written under a different provider): local\n    // sandboxes derive it at construction, remote sandboxes clone into the\n    // VM's own home so it resolves lazily at first start.\n    // `runSetupOn` references `runSessionSetup`, defined below — it is only\n    // invoked during start, long after this closure fully initializes.\n    const runSetupOn = (target: unknown, workdir: string) =>\n      runSessionSetup(requireExec(target as WorkspaceSandbox), workdir);\n    const guardedSetup = createSessionSetupHook(runSetupOn, session.id, repoFullName);\n    // Composed start hook: marker-guarded repo setup, then per-start","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/workspace.ts#L277-L313","documentation":"After fetching the project-repository link, createWorkspaceFactory loads its associated connection and repository in parallel. If either is missing, the link is considered incomplete and this Error is thrown — a workspace cannot be provisioned without both.","triggerScenarios":"The projectRepository row references a connectionId or repositoryId that no longer exists (connection revoked/deleted, repository record removed, or link created with dangling IDs).","commonSituations":"A GitHub App connection being uninstalled/deleted while links still point at it, partial cleanup scripts deleting repositories but not their links, or storage restored inconsistently.","solutions":["Recreate the missing connection or repository record, then create a fresh complete repository link and point the session at it","Check each piece: storage.connections.get and storage.repositories.get to identify which side is missing","Delete the orphaned projectRepository link to prevent further resolution attempts against it"],"exampleFix":"// before\nconst factory = await createWorkspaceFactory({ session }); // link's connection was deleted\n// after\nconst link = await storage.projectRepositories.get({ orgId: session.orgId, id: session.projectRepositoryId });\nconst conn = link && await storage.connections.get({ orgId: session.orgId, id: link.connectionId });\nif (!conn) {\n  const newConn = await reconnectGithubApp(); // recreate connection\n  const newLink = await storage.projectRepositories.create({ orgId: session.orgId, connectionId: newConn.id, repositoryId: link.repositoryId });\n}\nconst factory = await createWorkspaceFactory({ session });","handlingStrategy":"validation","validationCode":"const link = await storage.projectRepositories.get({ orgId: session.orgId, id: session.projectRepositoryId });\nif (link) {\n  const [conn, repo] = await Promise.all([\n    storage.connections.get({ orgId: session.orgId, id: link.connectionId }),\n    storage.repositories.get({ orgId: session.orgId, id: link.repositoryId }),\n  ]);\n  if (!conn || !repo) throw new Error('Repository link incomplete');\n}","typeGuard":"null","tryCatchPattern":"try {\n  factory = await createWorkspaceFactory({ session, user });\n} catch (e) {\n  if (e.message.includes('is incomplete')) {\n    // recreate the missing connection/repository and relink\n  } else throw e;\n}","preventionTips":["Delete dependent links when removing connections or repositories","Create links only from verified existing connection+repository pairs","Restore storage backups atomically so link references stay consistent"],"tags":["storage","missing-resource","github","consistency"],"backgroundTag":"referenced-record-not-found","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}