sinelaw/fresh · error

failed

Error message

${action} failed

What it means

The single-workspace archive/delete API runs the action through runLifecycleBatch and throws when the batch reports zero successes. It prefers res.lastErr — the real underlying error from the batch — and only falls back to the generic '${action} failed' message when no detail is available. Seeing the generic message means the batch failed without reporting a cause.

Solutions

  1. Read res.lastErr from the batch result if available — prefer the API path that surfaces it for diagnosis
  2. Retry the action after a short delay if the cause was a transient sync issue
  3. Verify the workspace state (already archived/deleted?) and skip if the action effectively completed

Example fix

// before
await api.archiveWorkspace(id);
// after
try {
  await api.archiveWorkspace(id);
} catch (e) {
  console.warn("archive failed:", e);
  await new Promise(r => setTimeout(r, 1000));
  await api.archiveWorkspace(id); // retry transient failures
}
Defensive patterns

Strategy: retry

Validate before calling

const ws = api.getWorkspace(id);
if (!ws) return false; // avoid calling lifecycle on a vanished workspace

Try / catch

try {
  await api.archiveWorkspace(id);
} catch (e) {
  const msg = String(e);
  if (msg.endsWith("archive failed") || msg.endsWith("delete failed")) {
    await new Promise(r => setTimeout(r, 1000));
    await api.archiveWorkspace(id); // retry once; no lastErr detail available
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the archive or delete verb for one workspace when runLifecycleBatch returns { ok: 0 } with no lastErr — e.g. a cross-machine manifest sync failure or an internal batch error without a recorded message.

Common situations: Manifest sync conflicts between machines; transient storage errors during archive/delete; calling with a workspace that passed eligibility but fails during actual processing.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/35b5751754398c4b. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/plugins/orchestrator.ts:10704

  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",
): Promise<boolean> {
  // Before `requireEligible`, which rejects an ineligible workspace.
  await yieldToCaller();
  const s = resolveWorkspace(target);
  if (!s) return false;
  requireEligible(s, action);
  const res = await runLifecycleBatch(action, [s.id]);
  if (res.ok === 0) {
    throw new Error(res.lastErr || `${action} failed`);
  }
  refreshOpenDialog();
  return true;
}

function apiListArchived(): ArchivedWorkspace[] {
  const out: ArchivedWorkspace[] = [];
  for (const { manifest } of scanArchiveManifests()) {
    for (const e of manifest.sessions) {
      out.push({
        archivedRoot: e.root,
        originalRoot: e.original_root,
        name: e.label,
        branch: e.branch,
        archivedAt: e.archived_at,
        projectPath: e.repo_root ?? "",
      });
    }

View on GitHub (pinned to 67894ca546)