odysseus-dev/odysseus · error · HTTPException
Action '{action}' requires admin privileges
Error message
Action '{action}' requires admin privileges What it means
Raised by the task-creation API when a task of type 'action' names an action classified in ADMIN_ONLY_TASK_ACTIONS (actions that run shell/SSH commands or serve cookbook surfaces) and the requesting owner does not pass owner_has_admin_task_privileges(user). It is a deliberate authorization boundary (review item CRIT-C) returning HTTP 403.
Source
Thrown at routes/task_routes.py:435
)
resumed += 1
db.commit()
finally:
db.close()
return {"ok": True, "opened": True, "enabled": bool(prefs.get("tasks_enabled")), "resumed": resumed}
# Actions that execute shell/SSH commands or cross into admin-only
# Cookbook serving surfaces — restricted to admins.
# Non-admin users cannot create tasks with these action types via the
# API. See review CRIT-C.
_ADMIN_ONLY_ACTIONS = ADMIN_ONLY_TASK_ACTIONS
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)View on GitHub (pinned to f9235ebbf1)
Solutions
- Run the task under an owner that has admin task privileges, or ask the instance admin to create/own the task.
- Filter the client-side action list to exclude ADMIN_ONLY_TASK_ACTIONS for non-admin users so the option is never submitted.
- If the deployment is single-user and the owner was wrongly detected, verify how _owner(request) derives the user and that the admin privilege list (owner_has_admin_task_privileges) includes that owner.
- If policy allows it, have the admin implement the capability as a safer non-admin action instead of relaxing the check.
Example fix
# before
actions = list(BUILTIN_ACTION_INFO.keys())
# after
from src.task_actions import ADMIN_ONLY_TASK_ACTIONS
is_admin = current_user_has_admin_privileges
actions = [a for a in BUILTIN_ACTION_INFO
if is_admin or a not in ADMIN_ONLY_TASK_ACTIONS] Defensive patterns
Strategy: validation
Validate before calling
const isAdmin = await api.getMe().then(u => u?.has_admin_task_privileges ?? false);
function canCreateAction(action, isAdmin) {
const ADMIN_ONLY = new Set(['action_run_local', 'ssh_command', 'run_script']); // keep in sync with ADMIN_ONLY_TASK_ACTIONS
return isAdmin || !ADMIN_ONLY.has(action);
} Try / catch
try {
await api.createTask(payload);
} catch (e) {
if (e.status === 403 && /admin privileges/.test(e.message)) {
showInfo('This action type is restricted to admins.');
return;
}
throw e;
} Prevention
- Fetch ADMIN_ONLY_TASK_ACTIONS from the server (or a /me privileges flag) and filter the action picker per role.
- Never rely on hiding the option alone — enforce server-side, which this route already does.
- In e2e tests, assert a non-admin token gets 403 for each admin-only action.
When it happens
Trigger: POST /api/tasks (or PUT update) with task_type='action' and action in the admin-only set (e.g. action_run_local, ssh_command, run_script) while authenticated as a non-admin owner; the check _require_admin_for_task_action fires before schedule validation.
Common situations: Self-hosted multi-user deployment where a regular user clones an admin's task; frontend offers all BUILTIN_ACTION_INFO entries in the picker without filtering by role; upgrading to a version that newly classifies an action as admin-only and previously-created task edits now fail.
Related errors
- Admin only
- Authentication required
- Cannot chain to another user's task
- Access denied
- Failed to update task
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/481ae864638852b7.
Report an issue: GitHub.