affaan-m/ECC · error

Invalid target version: ${targetVersion}

Error message

Invalid target version: ${targetVersion}

What it means

Thrown by rollbackTo() when targetVersion cannot be coerced to a positive integer. The function calls Number(targetVersion) and then rejects anything that is not an integer or is <= 0, because version snapshots are stored as vN.md files with N >= 1. A non-numeric or zero/negative value would either produce a bogus filename or roll back to nothing.

Source

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

    event: 'snapshot',
    version: nextVersion,
    reason: options.reason || null,
    author: options.author || null,
    status: 'applied',
    created_at: createdAt,
  });

  return {
    version: nextVersion,
    path: snapshotPath,
    created_at: createdAt,
  };
}

function rollbackTo(skillPath, targetVersion, options = {}) {
  const normalizedTargetVersion = Number(targetVersion);
  if (!Number.isInteger(normalizedTargetVersion) || normalizedTargetVersion <= 0) {
    throw new Error(`Invalid target version: ${targetVersion}`);
  }

  ensureSkillExists(skillPath);
  ensureSkillVersioning(skillPath);

  const targetPath = path.join(getVersionsDir(skillPath), `v${normalizedTargetVersion}.md`);
  if (!fs.existsSync(targetPath)) {
    throw new Error(`Version not found: v${normalizedTargetVersion}`);
  }

  const currentVersion = getCurrentVersion(skillPath);
  const targetContent = fs.readFileSync(targetPath, 'utf8');
  fs.writeFileSync(getSkillFilePath(skillPath), targetContent, 'utf8');

  const createdVersion = createVersion(skillPath, {
    timestamp: options.timestamp,
    reason: options.reason || `rollback to v${normalizedTargetVersion}`,
    author: options.author || null,

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Pass a positive integer (or numeric string of a positive integer) such as 2 or '2'.
  2. Strip a leading 'v' if your input uses the label format: targetVersion.replace(/^v/i, '').
  3. Use getCurrentVersion(skillPath) to discover the highest existing version, then pick a target strictly less than or equal to it.
  4. Validate user input upstream with a regex like /^[1-9][0-9]*$/ before calling rollbackTo.

Example fix

// before
rollbackTo(skillPath, 'v2');   // 'v2' -> Number('v2') = NaN -> Invalid target version

// after
const numericTarget = parseInt(String(targetVersion).replace(/^v/i, ''), 10);
rollbackTo(skillPath, numericTarget);
Defensive patterns

Strategy: validation

Validate before calling

function parseTargetVersion(raw) {
  if (typeof raw === 'string') raw = raw.replace(/^v/i, '');
  const n = Number(raw);
  if (!Number.isInteger(n) || n <= 0) return null;
  return n;
}

const target = parseTargetVersion(input);
if (target === null) throw new Error(`Bad target version: ${input}`);
rollbackTo(skillPath, target);

Type guard

function isPositiveVersion(value) {
  if (typeof value === 'string') value = value.replace(/^v/i, '');
  const n = Number(value);
  return Number.isInteger(n) && n > 0;
}

Try / catch

try {
  rollbackTo(skillPath, targetVersion);
} catch (error) {
  if (/Invalid target version/.test(error.message)) {
    // prompt the user or fall back to getCurrentVersion
    rollbackTo(skillPath, getCurrentVersion(skillPath));
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling rollbackTo(skillPath, 'latest'), rollbackTo(skillPath, 0), rollbackTo(skillPath, -1), rollbackTo(skillPath, 3.5), rollbackTo(skillPath, NaN), rollbackTo(skillPath, undefined), or rollbackTo(skillPath, '') — all fail the Number.isInteger check or the <= 0 guard.

Common situations: CLI accepts a user-supplied --to flag without validating it; reading a target version from a config that may be missing; off-by-one where the caller asks for v0 thinking versions are zero-indexed; passing the version label 'v3' instead of the number 3.

Related errors


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