{"record":{"id":"3cf0aad640b6fc18","repo":"HKUDS/Vibe-Trading","slug":"duplicate-skill-names-in-manifest-input-names","errorCode":null,"errorMessage":"duplicate skill names in manifest input: {names}","messagePattern":"duplicate skill names in manifest input: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/governance/manifest.py","lineNumber":407,"sourceCode":"            two records with the same name.\n    \"\"\"\n    if not run_id or not run_id.strip():\n        raise ValueError(\"run_id must not be empty\")\n    if not timestamp or not timestamp.strip():\n        raise ValueError(\"timestamp must not be empty\")\n\n    if isinstance(skills, Mapping):\n        skill_records = tuple(\n            sorted(\n                (SkillRecord.from_content(name, content) for name, content in skills.items()),\n                key=lambda record: record.name,\n            )\n        )\n    else:\n        skill_records = tuple(sorted(skills, key=lambda record: record.name))\n        names = [record.name for record in skill_records]\n        if len(names) != len(set(names)):\n            raise ValueError(f\"duplicate skill names in manifest input: {names}\")\n\n    tools_snapshot = ToolRegistrySnapshot.from_names(tool_names)\n    pv_pairs = _pairs(package_versions)\n    extra_pairs = _pairs(extra)\n\n    manifest_hash = _compute_manifest_hash(\n        system_prompt_hash=_hash_prefixed(system_prompt),\n        skills=skill_records,\n        tools=tools_snapshot,\n        package_versions=pv_pairs,\n        extra=extra_pairs,\n    )\n\n    return RunManifest(\n        run_id=run_id,\n        timestamp=timestamp,\n        system_prompt_hash=_hash_prefixed(system_prompt),\n        skills=skill_records,","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/governance/manifest.py#L389-L425","documentation":"When skills is passed as a sequence (not a Mapping), build_run_manifest sorts records by name and rejects duplicates — two SkillRecords with the same name make the manifest ambiguous.","triggerScenarios":"Calling build_run_manifest(skills=[SkillRecord(name=\"x\",...), SkillRecord(name=\"x\",...)]) with the same name appearing twice.","commonSituations":"Concatenating skill lists from multiple sources without deduping; case/normalization differences in generated names; tests seeding overlapping skill sets.","solutions":["Dedupe by name before calling, keeping the intended version (e.g. last-loaded wins)","If skills come from directories, detect collisions at load time rather than manifest time","Pass a Mapping keyed by skill name so duplication is structurally impossible"],"exampleFix":"# before\nskills = list(dir_a_skills) + list(dir_b_skills)  # both contain \"deploy\"\nbuild_run_manifest(..., skills=skills)\n# after\nmerged = {s.name: s for s in [*dir_a_skills, *dir_b_skills]}\nbuild_run_manifest(..., skills=merged)","handlingStrategy":"validation","validationCode":"skills_map = {}\nfor s in skills:\n    skills_map[s.name] = s  # last wins; or detect and raise with context\nnames = [s.name for s in skills]\nassert len(names) == len(set(names)), sorted({n for n in names if names.count(n) > 1})","typeGuard":"def has_unique_skill_names(skills: list) -> bool:\n    names = [s.name for s in skills]\n    return len(names) == len(set(names))","tryCatchPattern":"except ValueError as e: if 'duplicate skill names' in str(e): dedupe by name (decide precedence) and retry","preventionTips":["Pass skills as a Mapping keyed by name","Detect name collisions at skill-load time","Merge skill sources with explicit precedence rules"],"tags":["manifest","skills","duplicate-key","python"],"backgroundTag":"duplicate-key-in-input","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}