{"record":{"id":"f83a4ce6de8fae40","repo":"tinyhumansai/openhuman","slug":"task-task-id-cannot-depend-on-itself","errorCode":null,"errorMessage":"task {task_id} cannot depend on itself","messagePattern":"task (.+?) cannot depend on itself","errorType":"validation","errorClass":"TeamError","httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/orchestration/agent_teams/ops.rs","lineNumber":404,"sourceCode":"    })\n}\n\n/// Validate a new task's dependency edges against the team's existing tasks.\n///\n/// Rejects self-dependency, unknown dependency ids, and any edge that would\n/// introduce a cycle. The cycle check builds the full graph (existing tasks +\n/// the new task with its proposed deps) and runs Kahn's algorithm — the same\n/// shape used for workflow phase graphs in `workflow_runs::ops::has_cycle`.\nfn validate_dependencies(\n    new_task_id: &str,\n    depends_on: &[String],\n    existing: &[AgentTeamTask],\n) -> Result<()> {\n    let known: HashSet<&str> = existing.iter().map(|t| t.id.as_str()).collect();\n\n    for dep in depends_on {\n        if dep == new_task_id {\n            return Err(anyhow!(TeamError::SelfDependency {\n                task_id: new_task_id.to_string(),\n            }));\n        }\n        if !known.contains(dep.as_str()) {\n            return Err(anyhow!(TeamError::UnknownDependency {\n                depends_on: dep.clone(),\n            }));\n        }\n    }\n\n    if has_task_cycle(new_task_id, depends_on, existing) {\n        return Err(anyhow!(TeamError::CyclicDependency));\n    }\n\n    Ok(())\n}\n\n/// Kahn's-algorithm cycle check over the task dependency graph (existing tasks","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/orchestration/agent_teams/ops.rs#L386-L422","documentation":"Thrown by agent_teams::ops::validate_dependencies (reached via assign_task) when a new task's depends_on list contains the new task's own id. Self-dependency is rejected before any ledger write; it wraps TeamError::SelfDependency.","triggerScenarios":"Calling agent_teams assign_task with depends_on containing the very task_id being created — typically a template that interpolates the new id into its own dependency list, or a copy-paste of an existing task's deps onto a task renamed to a dep's id.","commonSituations":"Generated task graphs where the id and dep id come from the same variable; renaming a task to match one of its dependencies; LLM-authored dependency lists echoing the task id.","solutions":["Filter the new task's own id out of depends_on before submitting","Check the code/template that builds depends_on for an id-variable mix-up","Use distinct, meaningful task ids so deps never collide by construction"],"exampleFix":"// before\nlet depends_on = vec![task_id.clone()]; // self-dep -> error\nagent_teams::ops::assign_task(&config, &team_id, &task_id, member_id, &depends_on)?;\n\n// after — strip self-reference before submit\nlet depends_on: Vec<String> = depends_on.into_iter().filter(|d| d != &task_id).collect();\nagent_teams::ops::assign_task(&config, &team_id, &task_id, member_id, &depends_on)?;","handlingStrategy":"validation","validationCode":"let depends_on: Vec<String> = depends_on\n    .into_iter()\n    .filter(|d| d != &new_task_id)\n    .collect(); // strip self-reference before assign_task","typeGuard":"fn has_self_dependency(new_task_id: &str, depends_on: &[String]) -> bool {\n    depends_on.iter().any(|d| d == new_task_id)\n}","tryCatchPattern":null,"preventionTips":["Generate task ids and dependency lists from distinct variables so a template can never echo the id into its own deps","Validate depends_on client-side (no self-ref, all ids known) before every assign_task","Prefer unique, descriptive task ids over sequential ones that are easy to alias"],"tags":["agent-teams","tasks","dependencies","validation"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}