{"record":{"id":"c8ccf14dafe122f2","repo":"vercel/ai","slug":"cannot-write-harness-skill-skillname-skill","errorCode":null,"errorMessage":"Cannot write harness skill '${skillName}': ${skillDir} already exists and is not owned by the AI SDK harness.","messagePattern":"Cannot write harness skill '(.+?)': (.+?) already exists and is not owned by the AI SDK harness\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness/src/utils/write-skills.ts","lineNumber":382,"sourceCode":"\nasync function assertSkillDirectoryAvailable({\n  sandbox,\n  rootDir,\n  skillName,\n  abortSignal,\n}: {\n  sandbox: Experimental_SandboxSession;\n  rootDir: string;\n  skillName: string;\n  abortSignal?: AbortSignal;\n}): Promise<void> {\n  const skillDir = path.posix.join(rootDir, skillName);\n  const result = await sandbox.run({\n    command: `test ! -e ${shellQuote(skillDir)}`,\n    abortSignal,\n  });\n  if (result.exitCode !== 0) {\n    throw new Error(\n      `Cannot write harness skill '${skillName}': ${skillDir} already exists and is not owned by the AI SDK harness.`,\n    );\n  }\n}\n\nasync function removeSkillDirectories({\n  sandbox,\n  rootDir,\n  skillNames,\n  abortSignal,\n}: {\n  sandbox: Experimental_SandboxSession;\n  rootDir: string;\n  skillNames: ReadonlyArray<string>;\n  abortSignal?: AbortSignal;\n}): Promise<void> {\n  if (skillNames.length === 0) return;\n  const directories = Array.from(new Set(skillNames))","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness/src/utils/write-skills.ts#L364-L400","documentation":"Thrown by assertSkillDirectoryAvailable in packages/harness/src/utils/write-skills.ts when the target skill directory (<rootDir>/<skillName>) already exists in the sandbox and is not recorded as harness-owned in the skills manifest. The harness refuses to overwrite directories it did not create to avoid destroying user-managed content. The `test ! -e <dir>` probe returning non-zero means the path exists.","triggerScenarios":"Calling writeSkills with a skill whose name collides with an existing directory in rootDir that is absent from the harness manifest — e.g. a user-created folder, a skill installed by another tool, or a leftover directory whose manifest entry was deleted.","commonSituations":"Skill name collides with a pre-existing project folder (e.g. 'docs', 'test'); switching skill rootDir so the manifest no longer lists existing directories; a previous manifest was removed but skill dirs remain.","solutions":["Rename your skill to a non-conflicting name, or move/remove the existing directory in rootDir manually.","If the directory is a leftover from a previous harness run, remove it (and align the manifest) before writing.","Point rootDir at a dedicated directory (e.g. /workspace/.claude/skills) rather than a shared folder.","Check what exists at <rootDir>/<skillName> in the sandbox before writing."],"exampleFix":"// before\nawait writeSkills({ sandbox, rootDir: '/workspace', skills: [{ name: 'docs', files }] }); // /workspace/docs exists, not harness-owned\n// after\nawait writeSkills({ sandbox, rootDir: '/workspace', skills: [{ name: 'api-docs', files }] });","handlingStrategy":"validation","validationCode":"for (const skill of skills) {\n  const dir = `${rootDir}/${skill.name}`;\n  const check = await sandbox.run({ command: `test ! -e ${dir}` });\n  if (check.exitCode !== 0) throw new Error(`Path exists, move or rename before writing: ${dir}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await writeSkills({ sandbox, rootDir, skills });\n} catch (error) {\n  if (/already exists and is not owned by the AI SDK harness/.test((error as Error).message)) {\n    // surface a user-facing conflict error; do NOT auto-delete foreign directories\n    throw new Error(`Conflict: ${skillName} exists in ${rootDir}; remove or rename it first.`);\n  }\n  throw error;\n}","preventionTips":["Use a dedicated skills rootDir (e.g. /workspace/.claude/skills) instead of a shared folder.","Choose distinctive skill names unlikely to collide with project directories.","Clean up skill directories together with the manifest — never delete the manifest alone.","List rootDir contents before writing when adopting an existing workspace."],"tags":["sandbox","filesystem","conflict","harness","skills"],"backgroundTag":"path-already-exists","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}