{"record":{"id":"d6c632b70d7f3468","repo":"ReactiveX/rxjs","slug":"canonical-skill-root-is-not-a-directory-canonic","errorCode":null,"errorMessage":"Canonical Skill root is not a directory: ${canonicalSkillRoot}","messagePattern":"Canonical Skill root is not a directory: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/migrate/src/skill-install.ts","lineNumber":184,"sourceCode":"  return manageSkillInstallation(operation, installOptions);\n}\n\nfunction adapterFor(harness: SkillHarness): {\n  readonly adapter: SkillInstallAdapter;\n  readonly localPath: string;\n  readonly compatibleHarnesses: readonly SkillHarness[];\n} {\n  const adapter = skillHarnessAdapters[harness];\n  return { adapter: adapter.adapterId, localPath: adapter.targetDirectory, compatibleHarnesses: adapter.compatibleHarnesses };\n}\n\nasync function resolveInstallation(options: SkillInstallOptions): Promise<ResolvedInstallation> {\n  const projectRoot = resolve(options.projectRoot);\n  const canonicalProjectRoot = await realpath(projectRoot);\n  if (!(await stat(canonicalProjectRoot)).isDirectory()) throw new Error(`Project root is not a directory: ${projectRoot}`);\n\n  const canonicalSkillRoot = await realpath(resolve(options.canonicalSkillRoot));\n  if (!(await stat(canonicalSkillRoot)).isDirectory()) throw new Error(`Canonical Skill root is not a directory: ${canonicalSkillRoot}`);\n\n  const adapter = adapterFor(options.harness);\n  const targetPath = resolve(projectRoot, adapter.localPath);\n  assertContained(projectRoot, targetPath, 'Skill target must remain below the project root.');\n  const canonicalTargetPath = await canonicalFuturePath(targetPath);\n  assertContained(canonicalProjectRoot, canonicalTargetPath, 'Skill target resolves outside the project root.');\n  const integrity = await inspectSkillIntegrity(canonicalSkillRoot);\n  const provenance: SkillInstallProvenance = {\n    ...integrity,\n    installSchemaVersion: skillInstallSchemaVersion,\n    adapter: adapter.adapter,\n    compatibleHarnesses: adapter.compatibleHarnesses,\n    targetPath: adapter.localPath.replaceAll(sep, '/'),\n  };\n  return {\n    projectRoot,\n    canonicalSkillRoot,\n    targetPath,","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/ReactiveX/rxjs/blob/54796b38a57e6309f9861e174737479bb3f63f61/packages/migrate/src/skill-install.ts#L166-L202","documentation":"Thrown by resolveInstallation when options.canonicalSkillRoot resolves to an existing path that is not a directory. The canonical skill root is normally supplied internally by resolveCanonicalSkillRoot, so this usually indicates a broken package layout or a bad explicit override.","triggerScenarios":"Passing a custom canonicalSkillRoot option pointing at a file; or the packaged skill/ directory being replaced by a file in a mangled install.","commonSituations":"Programmatic use of manageSkillInstallation with a wrong canonicalSkillRoot; corrupted @rxjs/migrate install.","solutions":["Reinstall @rxjs/migrate to restore the skill directory","If calling programmatically, pass the directory returned by resolveCanonicalSkillRoot","Verify the path with stat before invoking"],"exampleFix":"// before\nmanageSkillInstallation('check', { harness: 'claude', canonicalSkillRoot: './SKILL.md', projectRoot: '.' });\n// after\nmanageSkillInstallation('check', { harness: 'claude', canonicalSkillRoot: await resolveCanonicalSkillRoot(), projectRoot: '.' });","handlingStrategy":"validation","validationCode":"import { stat } from 'node:fs/promises';\nif (!(await stat(skillRoot)).isDirectory()) throw new TypeError(`canonicalSkillRoot not a directory: ${skillRoot}`);","typeGuard":"const isDir = async (p: string) => (await stat(p)).isDirectory();","tryCatchPattern":null,"preventionTips":["Always derive canonicalSkillRoot from resolveCanonicalSkillRoot","Reinstall the package if assets look wrong"],"tags":["cli","filesystem","packaging","validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"54796b38a57e6309f9861e174737479bb3f63f61","analyzedAt":"2026-08-28T10:21:27.410Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}