odysseus-dev/odysseus · error · Error

Failed to stop task (${res.status})

Error message

Failed to stop task (${res.status})

What it means

Thrown by the tasks UI when POST /api/tasks/{id}/stop returns a non-2xx status. As with trigger, the client prefers the backend's JSON `detail` field; this literal is the fallback message. There is no special-casing for stop, so a 409 (not running) surfaces as the backend detail if present, otherwise the generic status message.

Source

Thrown at static/js/tasks.js:178

      if (data && data.detail) msg = data.detail;
    } catch (_) {}
    if (res.status === 409) msg = 'Task is already running';
    throw new Error(msg);
  }
}

async function _stopTask(id) {
  const res = await fetch(`${API_BASE}/api/tasks/${id}/stop`, {
    method: 'POST',
    credentials: 'same-origin',
  });
  if (!res.ok) {
    let msg = `Failed to stop task (${res.status})`;
    try {
      const data = await res.json();
      if (data && data.detail) msg = data.detail;
    } catch (_) {}
    throw new Error(msg);
  }
}

async function _fetchRuns(taskId, limit = 10) {
  const res = await fetch(`${API_BASE}/api/tasks/${taskId}/runs?limit=${limit}`, {
    credentials: 'same-origin',
  });
  if (!res.ok) return [];
  const data = await res.json();
  return data.runs || [];
}

let _outputTargets = null;
async function _fetchOutputTargets() {
  if (_outputTargets) return _outputTargets;
  try {
    const res = await fetch(`${API_BASE}/api/tasks/meta/output-targets`, { credentials: 'same-origin' });
    const data = await res.json();

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check the HTTP status: 404 = task gone (refresh list), 401/403 = re-authenticate, 409/400 = run already finished (harmless, refresh the run state)
  2. Refresh the task's status via GET /api/tasks before showing the stop control
  3. Inspect the server log if status is 500

Example fix

// before
if (!res.ok) {
  let msg = `Failed to stop task (${res.status})`;
  ...
// after — mirror the trigger path and humanize 409
if (!res.ok) {
  let msg = `Failed to stop task (${res.status})`;
  try { const data = await res.json(); if (data && data.detail) msg = data.detail; } catch (_) {}
  if (res.status === 409) msg = 'Task is not running';
Defensive patterns

Strategy: try-catch

Validate before calling

const task = _tasks.find(t => t.id === id);
if (!task || task.status !== 'active') { showToast('Task is not running'); return; }

Try / catch

try { await _stopTask(id); } catch (e) { if (/\(409\)/.test(e.message) || /not running/i.test(e.message)) return; uiModule?.showError(e.message); }

Prevention

When it happens

Trigger: Clicking Stop on a task that is not currently running, on a task id that no longer exists (404), with an expired session (401/403), or when the stop endpoint returns a non-JSON body so data.detail cannot be read.

Common situations: Stop button clicked from a stale UI where the run already finished; race between the run completing and the user clicking stop; auth cookie expired.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/8b08538eb09d6ded. Report an issue: GitHub.