mastra-ai/mastra · error
Repository link ${session.projectRepositoryId} is incomplete
Error message
Repository link ${session.projectRepositoryId} is incomplete What it means
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.
Source
Thrown at mastracode/factory/src/workspace.ts:295
}
if (!sandboxConfig || !github) {
throw new Error('GitHub and a sandbox callback are required to create a Factory session workspace');
}
const createSessionSandboxInstance = sandboxConfig;
const storage = github.sourceControlStorage;
const projectRepository = await storage.projectRepositories.get({
orgId: session.orgId,
id: session.projectRepositoryId,
});
if (!projectRepository) throw new Error(`Repository link ${session.projectRepositoryId} was not found`);
// The remaining reads only depend on the repository link — issue them in
// parallel instead of paying four sequential storage round-trips.
const [connection, repository] = await Promise.all([
storage.connections.get({ orgId: session.orgId, id: projectRepository.connectionId }),
storage.repositories.get({ orgId: session.orgId, id: projectRepository.repositoryId }),
]);
if (!connection || !repository) throw new Error(`Repository link ${session.projectRepositoryId} is incomplete`);
const installation = await storage.installations.get({ orgId: session.orgId, id: connection.installationId });
if (!installation) throw new Error(`GitHub installation ${connection.installationId} was not found`);
const repoFullName = repository.slug;
// Construct (or fetch) the session's memoized sandbox instance.
// Construction is cheap and side-effect-free by the callback contract —
// the VM is provisioned on `start()`, which only the materialization
// pipeline calls. The workdir is never persisted or trusted from storage
// or client input (the stale-workdir incident class came from reusing
// `session.sandboxWorkdir` written under a different provider): local
// sandboxes derive it at construction, remote sandboxes clone into the
// VM's own home so it resolves lazily at first start.
// `runSetupOn` references `runSessionSetup`, defined below — it is only
// invoked during start, long after this closure fully initializes.
const runSetupOn = (target: unknown, workdir: string) =>
runSessionSetup(requireExec(target as WorkspaceSandbox), workdir);
const guardedSetup = createSessionSetupHook(runSetupOn, session.id, repoFullName);
// Composed start hook: marker-guarded repo setup, then per-startView on GitHub (pinned to 75dd419e61)
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
Example fix
// before
const factory = await createWorkspaceFactory({ session }); // link's connection was deleted
// after
const link = await storage.projectRepositories.get({ orgId: session.orgId, id: session.projectRepositoryId });
const conn = link && await storage.connections.get({ orgId: session.orgId, id: link.connectionId });
if (!conn) {
const newConn = await reconnectGithubApp(); // recreate connection
const newLink = await storage.projectRepositories.create({ orgId: session.orgId, connectionId: newConn.id, repositoryId: link.repositoryId });
}
const factory = await createWorkspaceFactory({ session }); Defensive patterns
Strategy: validation
Validate before calling
const link = await storage.projectRepositories.get({ orgId: session.orgId, id: session.projectRepositoryId });
if (link) {
const [conn, repo] = await Promise.all([
storage.connections.get({ orgId: session.orgId, id: link.connectionId }),
storage.repositories.get({ orgId: session.orgId, id: link.repositoryId }),
]);
if (!conn || !repo) throw new Error('Repository link incomplete');
} Type guard
null
Try / catch
try {
factory = await createWorkspaceFactory({ session, user });
} catch (e) {
if (e.message.includes('is incomplete')) {
// recreate the missing connection/repository and relink
} else throw e;
} Prevention
- 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
When it happens
Trigger: The projectRepository row references a connectionId or repositoryId that no longer exists (connection revoked/deleted, repository record removed, or link created with dangling IDs).
Common situations: 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.
Related errors
- Repository link ${session.projectRepositoryId} was not found
- Version-control repository not found.
- Version-control installation not found.
- Project repository not found for this organization.
- Source-control connection not found for this organization.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/72d8ef4bcef27084.
Report an issue: GitHub.