affaan-m/ECC · error

skillPath is required

Error message

skillPath is required

What it means

Thrown by normalizeSkillDir in the versioning module (reached via getSkillFilePath and the version/amendment log writers) when skillPath is falsy or not a string. Identical contract to provenance.normalizeSkillDir: accepts a directory or a path ending in SKILL.md.

Source

Thrown at scripts/lib/skill-evolution/versioning.js:18

'use strict';

const fs = require('fs');
const path = require('path');

const { appendFile, ensureDir } = require('../utils');

const VERSION_DIRECTORY_NAME = '.versions';
const EVOLUTION_DIRECTORY_NAME = '.evolution';
const EVOLUTION_LOG_TYPES = Object.freeze([
  'observations',
  'inspections',
  'amendments',
]);

function normalizeSkillDir(skillPath) {
  if (!skillPath || typeof skillPath !== 'string') {
    throw new Error('skillPath is required');
  }

  const resolvedPath = path.resolve(skillPath);
  if (path.basename(resolvedPath) === 'SKILL.md') {
    return path.dirname(resolvedPath);
  }

  return resolvedPath;
}

function getSkillFilePath(skillPath) {
  return path.join(normalizeSkillDir(skillPath), 'SKILL.md');
}

function ensureSkillExists(skillPath) {
  const skillFilePath = getSkillFilePath(skillPath);
  if (!fs.existsSync(skillFilePath)) {
    throw new Error(`Skill file not found: ${skillFilePath}`);

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Pass the skill directory path (or the SKILL.md file path) as a non-empty string.
  2. Guard callers for undefined before invoking.
  3. Resolve the path once upstream and reuse it.

Example fix

// before
versioning.getSkillFilePath(skill?.dir);

// after
if (skill?.dir) versioning.getSkillFilePath(skill.dir);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof skillPath !== 'string' || skillPath.trim().length === 0) {
  throw new TypeError('skillPath must be a non-empty string');
}
versioning.getSkillFilePath(skillPath);

Type guard

function isNonEmptyString(v) {
  return typeof v === 'string' && v.trim().length > 0;
}

Try / catch

try {
  versioning.getSkillFilePath(skillPath);
} catch (err) {
  if (/skillPath is required/.test(err.message)) return null;
  throw err;
}

Prevention

When it happens

Trigger: Calling versioning.getSkillFilePath(undefined); versioning.appendObservation(null, ...); passing a skill config object instead of its path.

Common situations: Undefined loop/destructure variable; calling versioning APIs before resolving the skill directory; passing the SKILL.md contents instead of the path.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/ab988770e7b61d99. Report an issue: GitHub.