odysseus-dev/odysseus · error · Error

Failed to create task

Error message

Failed to create task

What it means

Thrown by _createTask in static/js/tasks.js:79 when POST /api/tasks returns non-2xx. The status and server detail are discarded — the caller only ever sees 'Failed to create task'. The backend router (routes/task_routes.py, prefix /api/tasks) validates the task body (action, schedule, prompt fields) and is behind the interactive/auth gates.

Source

Thrown at static/js/tasks.js:79

    await fetch(`${API_BASE}/api/tasks/onboarding`, {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ enabled: false }),
    });
  } catch (e) {
    console.warn('Tasks onboarding failed:', e);
  }
}

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',
  });

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Replay the POST in devtools and read the 422 body — it names the invalid field (schedule, action, etc.).
  2. Pick action values from /api/tasks/meta/actions rather than hardcoding.
  3. Validate the schedule format client-side before submit.
  4. Re-login on 401 and retry.

Example fix

// before
if (!res.ok) throw new Error('Failed to create task');
// after
const d = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(d.detail ? (typeof d.detail === 'string' ? d.detail : JSON.stringify(d.detail)) : `Failed to create task (HTTP ${res.status})`);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!data || typeof data !== 'object' || !data.action) { throw new Error('Task payload must include an action'); } // ideally fetch valid actions first: await fetch(`${API_BASE}/api/tasks/meta/actions`)

Type guard

/** @returns {boolean} task payload has the fields POST /api/tasks requires */
function isValidTaskPayload(p) {
  return !!p && typeof p === 'object' && typeof p.action === 'string' && p.action.length > 0
    && (p.schedule === undefined || typeof p.schedule === 'string');
}

Try / catch

try { return await _createTask(data); } catch (err) { showError(err.message); /* keep the compose form populated for retry */ }

Prevention

When it happens

Trigger: Creating a task from the tasks UI: POST JSON task definition. 422 when the payload shape is wrong (unknown action, invalid schedule expression, missing prompt), 401/403 when unauthenticated or gated (app.py gates /api/tasks), 500 on persistence failure.

Common situations: Invalid cron/schedule string; action name not in the server's registry; session expired; sending client-composed extra fields the schema forbids after a backend update.

Related errors


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