sinelaw/fresh · error

workspace has no agent process to stop

Error message

workspace has no agent process to stop

What it means

requireEligible guards the single-workspace lifecycle verbs against ineligibility: for the "stop" action it throws this error when the workspace has no agent process to stop (bulkEligible('stop', id) is false). The picker disables such buttons; programmatic callers get an explicit reason instead of a silent no-op.

Solutions

  1. Check eligibility before stopping (e.g. verify the workspace has a running agent) or skip ineligible targets
  2. Handle the already-stopped case as success in your script logic
  3. Re-resolve the workspace; if it was archived, unarchive it first before lifecycle verbs

Example fix

// before
api.stopWorkspace(id);
// after
if (api.getWorkspace(id)?.hasRunningAgent) {
  api.stopWorkspace(id);
}
Defensive patterns

Strategy: validation

Validate before calling

const stoppable = (ws) => ws && ws.state === "running" && ws.hasRunningAgent;
if (stoppable(api.getWorkspace(id))) api.stopWorkspace(id);

Type guard

function hasStoppableAgent(s) {
  return s != null && s.hasRunningAgent === true && s.archived !== true;
}

Try / catch

try {
  api.stopWorkspace(id);
} catch (e) {
  if (String(e).includes("no agent process to stop")) {
    // already stopped: treat as success
  } else throw e;
}

Prevention

When it happens

Trigger: Calling apiStopWorkspace(target) for a workspace whose session has no running agent process — already stopped, archived, or a placeholder session that never spawned a process.

Common situations: Double-clicking Stop so the second call races the first; scripting stop across a workspace list containing archived entries; stopping a workspace created but whose agent never launched.

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


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

Appendix: source

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

  refreshOpenDialog();
  return true;
}

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(

View on GitHub (pinned to 67894ca546)