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
- Check the status: 403 → only owners can delete; request ownership or have the owner delete
- 409 → stop dependent runs/resources first, then retry the delete
- Re-fetch the project list after handling; if the project is gone, the 404-tolerant behavior already succeeded
- For 5xx, verify project state on the server before retrying to detect partial cascades
- 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
- Confirm ownership before exposing the delete action (server returns 403 otherwise)
- Stop dependent runs/resources before delete to avoid 409s
- Invalidate the projects cache in a finally block regardless of outcome
- Disable the delete button while a delete is in flight to avoid duplicate requests
- Rely on the built-in 404 idempotency; don't add extra existence pre-checks that can race
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
- Failed to unlink repository (${res.status})
- await extractError(res)
- ${body?.error} or Failed to save GitHub token (${res.status}
- Failed to remove GitHub token (${res.status})
- Failed to list repos (${res.status})
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/2ebf49e5398f6398.
Report an issue: GitHub.