{"record":{"id":"d4ee1a0f2db1ef73","repo":"Hmbown/CodeWhale","slug":"duplicate-agent-profile-id","errorCode":null,"errorMessage":"duplicate agent profile id {}","messagePattern":"duplicate agent profile id (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/profile.rs","lineNumber":287,"sourceCode":"\npub fn load_agent_profile_identities_from_dir(\n    dir: impl AsRef<Path>,\n) -> Result<Vec<AgentProfileIdentity>> {\n    let dir = dir.as_ref();\n    agent_profile_paths(dir)?\n        .into_iter()\n        .map(|path| load_agent_profile_identity_file(&path))\n        .collect()\n}\n\npub fn load_agent_profiles_from_dir(dir: impl AsRef<Path>) -> Result<Vec<AgentProfile>> {\n    let dir = dir.as_ref();\n    let mut profiles = Vec::new();\n    let mut seen = BTreeSet::new();\n    for path in agent_profile_paths(dir)? {\n        let profile = load_agent_profile_file(&path)?;\n        if !seen.insert(profile.id.to_ascii_lowercase()) {\n            bail!(\"duplicate agent profile id {}\", profile.id);\n        }\n        profiles.push(profile);\n    }\n    Ok(profiles)\n}\n\nfn agent_profile_paths(dir: &Path) -> Result<Vec<PathBuf>> {\n    if !dir.exists() {\n        return Ok(Vec::new());\n    }\n    if !dir.is_dir() {\n        bail!(\"agent profile path {} is not a directory\", dir.display());\n    }\n\n    let mut paths = std::fs::read_dir(dir)\n        .with_context(|| format!(\"reading agent profile dir {}\", dir.display()))?\n        .collect::<std::io::Result<Vec<_>>>()\n        .with_context(|| format!(\"reading agent profile entries in {}\", dir.display()))?","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/profile.rs#L269-L305","documentation":"load_agent_profiles_from_dir loads every *.toml in the profile directory and enforces unique profile ids, compared case-insensitively via to_ascii_lowercase. A second file claiming an already-seen id bails, because worker profiles are addressed by id and a duplicate would make selection ambiguous.","triggerScenarios":"Two TOML files in the agent profile dir (including subdirectory-style names differing only by case or extension spelling) whose id fields lowercase to the same value, e.g. \"Reviewer\" and \"reviewer\".","commonSituations":"Copying a profile file to tweak it and forgetting to change id; case-only rename left behind on case-insensitive filesystems; syncing profiles from two machines into one dir.","solutions":["List ids across profile TOMLs in the directory (grep '^id') and rename or remove the duplicate","Remember the check is case-insensitive: 'Coder' and 'coder' collide","Keep one profile per file and name files after their id to prevent recurrence"],"exampleFix":"# before: profiles/coder.toml -> id = \"coder\"; profiles/coder-alt.toml -> id = \"Coder\"\n\n# after: profiles/coder.toml -> id = \"coder\"; profiles/reviewer.toml -> id = \"reviewer\"","handlingStrategy":"validation","validationCode":"use std::collections::BTreeSet;\n\nfn profile_ids_unique(paths: &[std::path::PathBuf]) -> Result<(), String> {\n    let mut seen = BTreeSet::new();\n    for p in paths {\n        let id = read_profile_id(p)?; // parse `id = \"...\"`\n        if !seen.insert(id.to_ascii_lowercase()) {\n            return Err(format!(\"duplicate profile id {id} in {}\", p.display()));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"if let Err(err) = load_agent_profiles_from_dir(&dir) {\n    if err.to_string().contains(\"duplicate agent profile id\") {\n        eprintln!(\"two profile TOMLs share an id (case-insensitive); rename one\");\n    }\n    return Err(err.into());\n}","preventionTips":["Name each profile file after its id and keep one profile per file","Check ids case-insensitively when syncing profiles from multiple machines","Run a duplicate-id lint in CI for checked-in profile directories"],"tags":["fleet","agent-profiles","duplicate-id","configuration","rust","codewhale"],"backgroundTag":"duplicate-config-identifier","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}