mastra-ai/mastra · error

Failed to delete Factory (${res.status})

Error message

Failed to delete Factory (${res.status})

What it means

deleteFactoryProject sends DELETE to `/web/factory/projects/{id}`; the server cascades deletion over the project's source-control connections. Any non-ok status except 404 throws this error — 404 is treated as success so removal is idempotent.

Source

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

  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;
}

/** Stage and commit all changes in a Factory session workspace. */
export async function commitChanges(
  baseUrl: string,
  projectRepositoryId: string,
  message: string,
  sessionId: string,
): Promise<CommitResult> {
  return postRepositoryGitOp<CommitResult>(baseUrl, projectRepositoryId, 'commit', { message, sessionId });
}

export interface PushResult {
  pushed: boolean;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check the status: 403 → only owners can delete; request ownership or have the owner delete
  2. 409 → stop dependent runs/resources first, then retry the delete
  3. Re-fetch the project list after handling; if the project is gone, the 404-tolerant behavior already succeeded
  4. For 5xx, verify project state on the server before retrying to detect partial cascades
  5. Ensure the id passed is the Factory project id from the API, not a local or display identifier

Example fix

// before
await deleteFactoryProject(baseUrl, id);
removeFromList(id);
// after
try {
  await deleteFactoryProject(baseUrl, id);
} catch (e) {
  if (/\(409\)/.test(e.message)) {
    await stopActiveRuns(id);
    await deleteFactoryProject(baseUrl, id);
  } else throw e;
} finally {
  queryClient.invalidateQueries(factoryProjectsKeys.all);
}
Defensive patterns

Strategy: try-catch

Validate before calling

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

Type guard

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

Try / catch

try {
  await deleteFactoryProject(baseUrl, factoryProjectId);
} catch (e) {
  if (isAlreadyDeleted(e)) {
    // treat as success
  } else if (/\((401|403)\)/.test(e.message)) {
    promptReauthOrOwnershipCheck();
  } else {
    throw e;
  }
} finally {
  queryClient.invalidateQueries(factoryProjectsKeys.all);
}

Prevention

When it happens

Trigger: Non-ok, non-404 response: 401 expired session, 403 current user is not the project owner, 409 project has active resources/runs blocking deletion, 400 invalid factoryProjectId, 5xx server error mid-cascade (partial deletion possible).

Common situations: Double-clicking delete (second call gets 404, silently fine); collaborator trying to delete a project they don't own; deleting from a stale UI list where the id no longer exists; self-hosted DB constraint failure during cascade.

Related errors


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