odysseus-dev/odysseus · error · HTTPException
Task not found
Error message
Task not found
What it means
POST /api/tasks/{task_id}/clear-cache returns 404 when no ScheduledTask row with that id exists. The lookup happens before ownership and action checks, so a wrong or deleted id fails here first.
Source
Thrown at routes/task_routes.py:580
async def get_notifications(request: Request):
"""Return and clear pending task-run notifications for the
current user. Anonymous callers get nothing (prevents
cross-tenant drain — see review CRIT-B)."""
user = _owner(request)
if not user:
return {"notifications": []}
notes = task_scheduler.pop_notifications(owner=user)
return {"notifications": notes}
@router.post("/{task_id}/clear-cache")
async def clear_task_cache(request: Request, task_id: str):
"""Clear derived cache for one built-in task."""
user = _owner(request)
db = SessionLocal()
try:
task = db.query(ScheduledTask).filter(ScheduledTask.id == task_id).first()
if not task:
raise HTTPException(404, "Task not found")
if user and task.owner != user:
raise HTTPException(403, "Access denied")
action = task.action or ""
finally:
db.close()
cache_tables = {
"summarize_emails": ("email_summaries",),
"draft_email_replies": ("email_ai_replies",),
"email_auto_translate": ("email_translations",),
"extract_email_events": ("email_calendar_extractions",),
"learn_sender_signatures": ("sender_signatures",),
"check_email_urgency": ("email_tags", "email_urgency_alerts"),
}
tables = cache_tables.get(action)
if not tables:
raise HTTPException(400, "This task has no clearable cache")
View on GitHub (pinned to f9235ebbf1)
Solutions
- Re-fetch the task list and use a current task_id (GET /api/tasks/{id} must return 200 first).
- Handle 404 in the client by refreshing or dropping the stale task row from view.
- Make cache-clearing flows idempotent: treat 404 as 'nothing to clear' if that matches UX intent.
Example fix
# before
client.post(f"/api/tasks/{cached_id}/clear-cache")
# after
if client.get(f"/api/tasks/{cached_id}").status_code == 200:
client.post(f"/api/tasks/{cached_id}/clear-cache")
else:
refresh_task_list() Defensive patterns
Strategy: validation
Validate before calling
async function taskExists(id) {
const res = await fetch(`/api/tasks/${encodeURIComponent(id)}`);
return res.status === 200;
} Try / catch
try { await api.clearCache(taskId); }
catch (e) {
if (e.status === 404) { removeTaskFromView(taskId); return; } // idempotent clear
throw e;
} Prevention
- Only show the Clear-cache action on rows from the current task list response.
- Treat 404 as success (nothing to clear) for idempotent maintenance flows.
- Re-sync the task list after deletions in other sessions/tabs.
When it happens
Trigger: POST clear-cache with a task_id that was deleted, a typo/truncated id, or an id from a different instance/environment.
Common situations: UI list is stale after the task was removed elsewhere; bookmarked deep-link to a removed task; copy-paste between dev and prod databases; retrying an old request after task deletion.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/3856d267b04ddddb.
Report an issue: GitHub.