odysseus-dev/odysseus · error · HTTPException

Cannot chain to another user's task

Error message

Cannot chain to another user's task

What it means

Ownership guard on task chaining: the chained target task exists but belongs to a different owner than the requesting user, so linking would let one user trigger another's tasks. Returned as HTTP 403.

Solutions

  1. Chain only to tasks owned by the same account.
  2. Restrict the client's chain-target picker to the current user's task list (GET /api/tasks already scopes by owner).
  3. If cross-user chaining is a legitimate feature request, it needs an explicit server-side grant mechanism, not removal of this check.

Example fix

// before
const target = allTasks.find(t => t.name === 'Admin Cleanup'); // may be another user's

// after
const myTasks = await api.listTasks(); // owner-scoped
const target = myTasks.find(t => t.name === 'Cleanup');
if (!target) throw new Error('Pick one of your own tasks');
Defensive patterns

Strategy: validation

Validate before calling

async function canChainTo(thenTaskId) {
  const res = await fetch(`/api/tasks/${encodeURIComponent(thenTaskId)}`);
  if (res.status === 403) return false; // not yours
  return res.ok;
}

Try / catch

try { await api.createTask(p); }
catch (e) {
  if (e.status === 403 && /another user/.test(e.message)) { showOwnTasksOnly(); return; }
  throw e;
}

Prevention

When it happens

Trigger: POST/PUT a task with then_task_id equal to an id owned by another account on a multi-owner instance. The earlier helper only applies the owner filter when user is set, so this explicit owner != user comparison is the authoritative cross-owner block.

Common situations: Multi-user deployment where a user discovers/guesses another's task id; admin-created tasks being chained by regular users; shared ids in documentation or exported configs; migrating tasks between accounts leaving stale chain references.

Related errors


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

Appendix: source

Thrown at routes/task_routes.py:527

        task_id = str(uuid.uuid4())
        db = SessionLocal()
        try:
            then_task_id = _validate_then_task_id(db, req.then_task_id, user)
            notifications_enabled = (
                False if req.task_type == "action" and req.notifications_enabled is None
                else bool(req.notifications_enabled) if req.notifications_enabled is not None
                else True
            )
            # Validate chained task belongs to same owner
            if req.then_task_id:
                chain_target = db.query(ScheduledTask).filter(
                    ScheduledTask.id == req.then_task_id
                ).first()
                if not chain_target:
                    raise HTTPException(400, "Chained task not found")
                if chain_target.owner != user:
                    raise HTTPException(403, "Cannot chain to another user's task")
            task = ScheduledTask(
                id=task_id,
                owner=user,
                name=name,
                prompt=req.prompt,
                task_type=req.task_type,
                action=req.action,
                schedule=req.schedule,
                scheduled_time=req.scheduled_time,
                scheduled_day=req.scheduled_day,
                scheduled_date=sched_date,
                cron_expression=req.cron_expression,
                trigger_type=req.trigger_type,
                trigger_event=req.trigger_event,
                trigger_count=req.trigger_count,
                trigger_counter=0,
                next_run=next_run,
                status="active" if (req.trigger_type in ("event", "webhook") or next_run) else "completed",

View on GitHub (pinned to f9235ebbf1)