sinelaw/fresh · error
workspace cannot be
Error message
workspace cannot be ${action === "archive" ? "archived" : "deleted"} What it means
requireEligible throws this for the "archive" and "delete" bulk actions when the workspace is not eligible for the requested verb — the message reads 'workspace cannot be archived' or 'workspace cannot be deleted'. As with stop, the goal is to give programmatic callers the reason a picker would have shown by disabling the button.
Solutions
- Stop the workspace's agent before requesting archive/delete
- Check eligibility (bulkEligible or an equivalent status read) before calling
- Skip or log already-archived/ineligible targets instead of retrying
Example fix
// before await api.archiveWorkspace(id); // after const ws = api.getWorkspace(id); if (ws?.hasRunningAgent) api.stopWorkspace(id); await api.archiveWorkspace(id);
Defensive patterns
Strategy: validation
Validate before calling
const ws = api.getWorkspace(id);
if (!ws || ws.pending || ws.archived || ws.hasRunningAgent) {
throw new Error(`workspace ${id} not eligible for ${action}`);
} Type guard
function isLifecycleEligible(s, action) {
return s != null && !s.pending && !s.archived &&
(action === "stop" ? s.hasRunningAgent === true : true);
} Try / catch
try {
await api.archiveWorkspace(id);
} catch (e) {
if (String(e).includes("cannot be archived")) {
api.stopWorkspace(id);
await api.archiveWorkspace(id);
} else throw e;
} Prevention
- Stop running agents before archive/delete
- Filter out pending and already-archived workspaces in bulk operations
- Check eligibility via status reads instead of blind retry
- Map the verb in the message ('archived'/'deleted') back to your intended action when logging
When it happens
Trigger: Calling the archive/delete API (apiArchiveWorkspace/apiDeleteWorkspace) on a workspace for which bulkEligible returns false — e.g. a still-running agent that must be stopped first, a pending/placeholder session, or an already-archived workspace for a second archive.
Common situations: Bulk scripts that archive workspaces without stopping running agents; retry loops re-archiving an already-archived workspace; deleting a workspace in a mid-creation pending state.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- workspace is still being created and cannot be
- workspace has no agent process to stop
- no such folder
- folder name must not be empty
- no such folder
AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13).
Data as JSON: /api/errors/997fbd1076906b9f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/fresh-editor/plugins/orchestrator.ts:10677
}
function apiDeleteFolder(folderId: string): boolean {
if (!folderById(folderId)) return false;
// `deleteFolder` reparents the subtree one level up, so nothing inside is
// lost — same as the dock's "Delete Folder".
deleteFolder(folderId);
refreshDockTree();
return true;
}
/// Shared guard for the three lifecycle verbs: the picker disables a button
/// it cannot honour, and a caller deserves the reason rather than a no-op.
function requireEligible(s: AgentSession, action: BulkAction): void {
if (bulkEligible(action, s.id)) return;
if (action === "stop") {
throw new Error("workspace has no agent process to stop");
}
throw new Error(`workspace cannot be ${action === "archive" ? "archived" : "deleted"}`);
}
function apiStopWorkspace(target: string | number): boolean {
const s = resolveWorkspace(target);
if (!s) return false;
requireEligible(s, "stop");
// Same batch runner as the picker's confirmed Stop, over one id.
if (runStopBatch([s.id]) === 0) throw new Error("stop failed");
refreshOpenDialog();
return true;
}
/// Archive / delete over a single workspace, through the same batch runner
/// the picker's confirmed action uses — so the cross-machine manifest sync,
/// and anything added to that path later, applies to a scripted archive too.
async function runLifecycle(
target: string | number,
action: "archive" | "delete",View on GitHub (pinned to 67894ca546)