{"record":{"id":"fbed497d52b4bee9","repo":"vercel/ai","slug":"duplicate-skill-name-skills-index-name","errorCode":null,"errorMessage":"Duplicate skill name: ${skills[index]!.name}","messagePattern":"Duplicate skill name: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness/src/utils/write-skills.ts","lineNumber":457,"sourceCode":"  errorMessage,\n}: {\n  sandbox: Experimental_SandboxSession;\n  command: string;\n  abortSignal?: AbortSignal;\n  errorMessage: string;\n}): Promise<void> {\n  const result = await sandbox.run({ command, abortSignal });\n  if (result.exitCode !== 0) {\n    throw new Error(\n      `${errorMessage} (exit ${result.exitCode})${result.stderr ? `: ${result.stderr}` : ''}`,\n    );\n  }\n}\n\nfunction assertUniqueSkillNames(skills: ReadonlyArray<ProjectedSkill>): void {\n  for (let index = 1; index < skills.length; index++) {\n    if (skills[index - 1]!.name === skills[index]!.name) {\n      throw new Error(`Duplicate skill name: ${skills[index]!.name}`);\n    }\n  }\n}\n\nfunction isSafeManifestSkillName(name: string): boolean {\n  SAFE_MANIFEST_SKILL_NAME.lastIndex = 0;\n  const matches = SAFE_MANIFEST_SKILL_NAME.test(name);\n  SAFE_MANIFEST_SKILL_NAME.lastIndex = 0;\n  return matches && name !== '.' && name !== '..' && !name.includes('/');\n}\n\nfunction validateSkillName({\n  name,\n  pattern,\n  message,\n}: {\n  name: string;\n  pattern: RegExp;","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness/src/utils/write-skills.ts#L439-L475","documentation":"Thrown by assertUniqueSkillNames in packages/harness/src/utils/write-skills.ts when two or more skills passed to writeSkills project to the same name. Duplicate directory names cannot be written to the same rootDir, so the harness rejects the input before touching the filesystem. The skills array is sorted by name first, so duplicates are detected by adjacent comparison.","triggerScenarios":"Calling writeSkills with a skills array containing two HarnessV1Skill entries whose `name` values are identical (after projection/naming), e.g. the same skill object passed twice or two skills generated from overlapping sources.","commonSituations":"Programmatic skill collection that merges lists without deduplicating; skills loaded from multiple directories with identical folder names; passing the same skill definition under two providers/configs.","solutions":["Deduplicate the skills array by name before calling writeSkills (e.g. with a Map keyed by name).","Rename one of the conflicting skills so names are unique.","Log the full skill name list to find which entries collide.","Guard the input at construction time so duplicate names never reach writeSkills."],"exampleFix":"// before\nawait writeSkills({ sandbox, rootDir, skills: [skillA, skillA] });\n// after\nconst unique = [...new Map(skills.map(s => [s.name, s])).values()];\nawait writeSkills({ sandbox, rootDir, skills: unique });","handlingStrategy":"validation","validationCode":"const names = skills.map(s => s.name);\nconst dupes = names.filter((n, i) => names.indexOf(n) !== i);\nif (dupes.length) throw new Error(`Duplicate skill names: ${[...new Set(dupes)].join(', ')}`);","typeGuard":null,"tryCatchPattern":"try {\n  await writeSkills({ sandbox, rootDir, skills });\n} catch (error) {\n  if (/Duplicate skill name:/.test((error as Error).message)) {\n    const unique = [...new Map(skills.map(s => [s.name, s])).values()];\n    await writeSkills({ sandbox, rootDir, skills: unique });\n  } else throw error;\n}","preventionTips":["Deduplicate skills by name at collection time (Map/Set by name).","Enforce unique names where skills are defined, not at the writeSkills boundary.","When merging skill lists from multiple sources, log dropped duplicates.","Add a unit test asserting skill name uniqueness in your skill registry."],"tags":["validation","skills","harness","duplicate"],"backgroundTag":"duplicate-name-conflict","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}