odysseus-dev/odysseus · error · Error

Failed to update task

Error message

Failed to update task

What it means

Thrown by _updateTask (static/js/tasks.js:90) when PUT /api/tasks/{id} returns non-2xx; message is the generic 'Failed to update task' with no status. Tests (test_task_cookbook_admin_gate.py, test_task_chain_owner_scope.py) show this route enforces ownership and admin gating, so 403 is a first-class outcome, not an anomaly.

Source

Thrown at static/js/tasks.js:90

async function _createTask(data) {
  const res = await fetch(`${API_BASE}/api/tasks`, {
    method: 'POST',
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
  if (!res.ok) throw new Error('Failed to create task');
  return await res.json();
}

async function _updateTask(id, data) {
  const res = await fetch(`${API_BASE}/api/tasks/${id}`, {
    method: 'PUT',
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
  if (!res.ok) throw new Error('Failed to update task');
  return await res.json();
}

async function _deleteTask(id) {
  const res = await fetch(`${API_BASE}/api/tasks/${id}`, {
    method: 'DELETE', credentials: 'same-origin',
  });
  if (!res.ok) throw new Error('Failed to delete task');
}

function _taskCardById(id) {
  const safe = (window.CSS && CSS.escape) ? CSS.escape(String(id)) : String(id).replace(/"/g, '\\"');
  return document.querySelector(`.task-card[data-id="${safe}"]`);
}

function _animateTaskRemoval(ids) {
  const cards = ids.map(_taskCardById).filter(Boolean);
  if (!cards.length) return Promise.resolve();

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check status: 403 → you are not the owner, refresh to see current state; 404 → task gone, drop the card and reload; 422 → fix the flagged field.
  2. Re-login on 401.
  3. Include the status and server detail in the thrown message for diagnosability.
  4. Reload the task list after any 404/403 instead of retrying blindly.

Example fix

// before
if (!res.ok) throw new Error('Failed to update task');
// after
const d = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(d.detail || `Failed to update task (HTTP ${res.status})`);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!id) { throw new Error('Task id required'); }

Try / catch

try { await _updateTask(id, data); } catch (err) { if (/HTTP 404|Failed to update/.test(err.message)) await refreshTasks(); showError(err.message); }

Prevention

When it happens

Trigger: Editing/saving a task card: PUT the task JSON. 404 when the task id no longer exists (deleted elsewhere), 403 when the user does not own the task or lacks rights, 422 on invalid field values, 401 on expired session.

Common situations: Task deleted in another tab or by the agent; non-owner trying to edit a shared instance; stale page holding an id that was recreated; schedule/action validation same as create.

Related errors


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