{"record":{"id":"4b486df8344ca55d","repo":"tinyhumansai/openhuman","slug":"duplicate-member-name-name","errorCode":null,"errorMessage":"duplicate member name: {name}","messagePattern":"duplicate member name: (.+?)","errorType":"validation","errorClass":"TeamError","httpStatus":null,"severity":"warning","filePath":"src/openhuman/agent/orchestration/agent_teams/ops.rs","lineNumber":54,"sourceCode":"/// Create a team and its initial members.\n///\n/// Rejects duplicate member names ([`TeamError::DuplicateMemberName`]).\npub fn create_team(\n    config: &Config,\n    lead_agent_id: &str,\n    parent_thread_id: Option<&str>,\n    summary: Option<&str>,\n    members: &[NewMember],\n) -> Result<TeamView> {\n    log::debug!(\n        \"{LOG_PREFIX} create_team.entry lead={lead_agent_id} members={}\",\n        members.len()\n    );\n\n    let mut seen: HashSet<&str> = HashSet::new();\n    for member in members {\n        if !seen.insert(member.name.as_str()) {\n            return Err(anyhow!(TeamError::DuplicateMemberName {\n                name: member.name.clone(),\n            }));\n        }\n    }\n\n    let team_id = format!(\"team-{}\", Uuid::new_v4().simple());\n    run_ledger::upsert_agent_team(\n        &config.workspace_dir,\n        AgentTeamUpsert {\n            id: team_id.clone(),\n            parent_thread_id: parent_thread_id.map(str::to_string),\n            lead_agent_id: lead_agent_id.to_string(),\n            status: AgentTeamStatus::Active,\n            summary: summary.map(str::to_string),\n            created_at: None,\n            closed_at: None,\n        },\n    )?;","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/orchestration/agent_teams/ops.rs#L36-L72","documentation":"Thrown by agent_teams create_team when two or more members in the same create request share the same name. The op builds a HashSet over member.name and rejects the whole request with TeamError::DuplicateMemberName before any row is written — no partial team is created. Member names must be unique within a team (they are how members address each other in message_member), while ids are generated.","triggerScenarios":"Calling agent_team_create with a members array containing two entries whose name field matches (e.g. two 'researcher' workers); case matters as written — exact string comparison; LLM-generated rosters commonly duplicate role-style names.","commonSituations":"Agent composes a team template like [researcher, researcher, writer] for parallel workers; copy-paste of member blocks; names differing only by whitespace.","solutions":["Deduplicate or number the member names in the create payload (researcher-1, researcher-2)","Validate the payload against the members array before submitting"],"exampleFix":"// before\n{\"members\":[{\"name\":\"researcher\",\"agent_id\":\"a1\"},{\"name\":\"researcher\",\"agent_id\":\"a2\"}]} // -> duplicate member name: researcher\n// after\n{\"members\":[{\"name\":\"researcher-1\",\"agent_id\":\"a1\"},{\"name\":\"researcher-2\",\"agent_id\":\"a2\"}]}","handlingStrategy":"validation","validationCode":"let names: HashSet<&str> = HashSet::new();\nfor m in &members { if !names.insert(m.name.as_str()) { return Err(anyhow!(\"duplicate member name: {}\", m.name)); } }\n// dedupe before agent_team_create:\nlet mut seen = HashSet::new();\nlet deduped: Vec<_> = members.into_iter().filter(|m| seen.insert(m.name.clone())).collect();","typeGuard":"fn unique_member_names(members: &[NewMember]) -> bool {\n    let mut seen = HashSet::new();\n    members.iter().all(|m| seen.insert(m.name.as_str()))\n}","tryCatchPattern":"match create_team(&config, lead, parent, summary, &members).await {\n    Err(e) if e.to_string().contains(\"duplicate member name\") => {\n        // normalize names (append -1/-2) and re-submit — nothing was created on failure\n        unimplemented!()\n    }\n    other => other,\n}","preventionTips":["Number role-style member names when spawning parallel workers (researcher-1, researcher-2)","Validate the roster client-side before submitting — the server rejects atomically, so no cleanup is needed"],"tags":["rust","agent-teams","validation","orchestration"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}