odysseus-dev/odysseus · error · HTTPException
Chained task not found
Error message
Chained task not found
What it means
Raised by _validate_then_task_id when then_task_id names a task that does not exist in the ScheduledTask table (optionally additionally filtered by owner). The chained target must already exist before it can be referenced. Returned as HTTP 404.
Source
Thrown at routes/task_routes.py:448
def _is_admin(user: str | None) -> bool:
return owner_has_admin_task_privileges(user)
def _require_admin_for_task_action(user: str | None, task_type: str | None, action: str | None) -> None:
if is_admin_only_task_action(task_type, action) and not _is_admin(user):
raise HTTPException(403, f"Action '{action}' requires admin privileges")
def _validate_then_task_id(db, then_task_id: Optional[str], user: Optional[str], current_task_id: Optional[str] = None) -> Optional[str]:
target_id = (then_task_id or "").strip()
if not target_id:
return None
if current_task_id and target_id == current_task_id:
raise HTTPException(400, "Task cannot chain to itself")
q = db.query(ScheduledTask).filter(ScheduledTask.id == target_id)
if user:
q = q.filter(ScheduledTask.owner == user)
target = q.first()
if not target:
raise HTTPException(404, "Chained task not found")
return target.id
@router.post("")
async def create_task(request: Request, req: TaskCreate):
user = _owner(request)
# Validate
if req.task_type in ("llm", "research") and not req.prompt:
raise HTTPException(400, "Prompt is required for LLM/research tasks")
if req.task_type == "action" and not req.action:
raise HTTPException(400, "Action name is required for action tasks")
# Block shell-executing action types for non-admins. action_run_local
# uses subprocess.run(shell=True) and ssh_command / run_script run
# arbitrary commands.
_require_admin_for_task_action(user, req.task_type, req.action)
if req.trigger_type == "schedule" and not req.schedule:
raise HTTPException(400, "Schedule is required for schedule-triggered tasks")
if req.trigger_type == "schedule" and req.schedule == "cron" and not req.cron_expression:View on GitHub (pinned to f9235ebbf1)
Solutions
- Verify the target id exists: GET /api/tasks/{then_task_id} must return 200 before submitting the chain.
- If it returns 404/403, re-create the target task or pick a task owned by the same user.
- Create the target task first, capture its id from the response, then set then_task_id on the parent.
Example fix
# before
task = client.create_task({...})
client.create_task({..., "then_task_id": guessed_id})
# after
child = client.create_task({...})
assert child["id"], "child task must exist"
parent = client.create_task({..., "then_task_id": child["id"]}) Defensive patterns
Strategy: validation
Validate before calling
async function resolveChainTarget(thenTaskId, owner) {
if (!thenTaskId) return null;
const res = await fetch(`/api/tasks/${encodeURIComponent(thenTaskId.trim())}`);
if (res.status === 404) throw new ValidationError('Chained task not found');
if (res.status === 403) throw new ValidationError('Chained task belongs to another user');
return thenTaskId.trim();
} Try / catch
try { await api.createTask(payload); }
catch (e) {
if (e.status === 404 && /Chained task/.test(e.message)) { refreshTaskList(); return; }
throw e;
} Prevention
- Populate chain targets from the live owner-scoped task list, not stored ids.
- Create the child task first and use the id from its response.
- Handle task-deleted events by pruning stale then_task_id references in local state.
When it happens
Trigger: POST/PUT a task with then_task_id set to an id that was deleted, belongs to another owner (when the owner filter applies), or is a typo/truncated paste (leading/trailing spaces are stripped, so mismatch must be in the id itself).
Common situations: Chaining to a task deleted earlier; two browser tabs where the target task was removed; copying task ids between instances/environments where ids differ; race where the target task is created asynchronously and referenced before commit.
Related errors
- Task cannot chain to itself
- Failed to create task
- No documents found
- Version not found
- Prompt is required for LLM/research tasks
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/c8deeb0938371c6a.
Report an issue: GitHub.