mastra-ai/mastra · error

Failed to unlink repository (${res.status})

Error message

Failed to unlink repository (${res.status})

What it means

unlinkRepository sends DELETE to `/web/factory/projects/{id}/repositories/{repoId}` and throws this error for any non-ok status except 404, which is deliberately tolerated so unlinking stays idempotent. It means an existing repository link could not be removed.

Source

Thrown at mastracode/factory-ui/src/ui/domains/workspaces/services/github.ts:444

    'Failed to link GitHub repository',
  );
  return toLinkedRepositoryPayload({ id: factoryProjectId, name: repo.fullName }, projectRepository);
}

/**
 * Unlink a repository from its Factory project. Missing links are treated as
 * already removed so unlink stays idempotent.
 */
export async function unlinkRepository(
  baseUrl: string,
  factoryProjectId: string,
  projectRepositoryId: string,
): Promise<void> {
  const res = await fetch(
    `${baseUrl}/web/factory/projects/${encodeURIComponent(factoryProjectId)}/repositories/${encodeURIComponent(projectRepositoryId)}`,
    { method: 'DELETE', credentials: 'include', headers: { Accept: 'application/json' } },
  );
  if (!res.ok && res.status !== 404) throw new Error(`Failed to unlink repository (${res.status})`);
}

/**
 * Delete a Factory project. The server cascades over its source-control
 * connections (and their repository links). Missing projects are treated as
 * already deleted so removal stays idempotent.
 */
export async function deleteFactoryProject(baseUrl: string, factoryProjectId: string): Promise<void> {
  const res = await fetch(`${baseUrl}/web/factory/projects/${encodeURIComponent(factoryProjectId)}`, {
    method: 'DELETE',
    credentials: 'include',
    headers: { Accept: 'application/json' },
  });
  if (!res.ok && res.status !== 404) throw new Error(`Failed to delete Factory (${res.status})`);
}

export interface CommitResult {
  committed: boolean;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check the status in the message: 401/403 → re-authenticate or confirm project membership
  2. Ensure projectRepositoryId belongs to the given factoryProjectId and both ids are the server-side ids (not display names)
  3. Treat as best-effort: catch and refresh the project's repository list to see the actual state
  4. For 5xx, retry after a short delay; cascade cleanup may be retried safely
  5. Do not change code to also swallow 409/400 — those indicate a real mismatch that should surface

Example fix

// before
await unlinkRepository(baseUrl, projectId, repoId);
// after
try {
  await unlinkRepository(baseUrl, projectId, repoId);
} catch (e) {
  if (/\(40[13]\)/.test(e.message)) {
    await reauth();
    return unlinkRepository(baseUrl, projectId, repoId);
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

function canAttemptUnlink(ids: { factoryProjectId?: unknown; projectRepositoryId?: unknown }): boolean {
  return typeof ids.factoryProjectId === 'string' && ids.factoryProjectId.length > 0 && typeof ids.projectRepositoryId === 'string' && ids.projectRepositoryId.length > 0;
}

Type guard

const isBenignDeleteError = (e: unknown): boolean => e instanceof Error && /\(404\)/.test(e.message);

Try / catch

try {
  await unlinkRepository(baseUrl, factoryProjectId, projectRepositoryId);
} catch (e) {
  if (isBenignDeleteError(e)) {
    // already unlinked: refresh and continue
  } else if (/\((401|403)\)/.test(e.message)) {
    promptReauth();
  } else {
    toast.error(e.message);
  }
} finally {
  queryClient.invalidateQueries(projectRepositoriesKey(factoryProjectId));
}

Prevention

When it happens

Trigger: Non-ok, non-404 response: 401 unauthenticated session, 403 user lacks access to the Factory project, 400/422 malformed factoryProjectId or projectRepositoryId, 409 the link is referenced/locked, 5xx server failure during cascade cleanup.

Common situations: Unlinking while another user already deleted the project (404 — correctly swallowed); permissions changed so the current user is no longer a project member; expired session; passing a repo id from a different project.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/939a638bb254005d. Report an issue: GitHub.