affaan-m/ECC · error · Error
Invalid target version
Error message
Invalid target version: ${targetVersion} What it means
rollbackTo converts targetVersion to a number and requires it to be a positive integer before rolling back. Versions are 1-based integers matching stored files v<n>.md, so non-integers, zero, negatives, and non-numeric input are all rejected. The original input is echoed in the message.
Solutions
- Pass a positive integer, e.g. rollbackTo(skillPath, 2)
- Coerce with Number() and validate Number.isInteger(v) && v > 0 before calling
- List available version files (v*.md in the versions dir) and pick a valid one
- Fix off-by-one logic where a 0-based index was passed instead of a 1-based version
Example fix
// before await rollbackTo(skillPath, 0); // after const v = Number(targetVersion); if (Number.isInteger(v) && v > 0) await rollbackTo(skillPath, v);
Defensive patterns
Strategy: validation
Validate before calling
const v = Number(targetVersion);
if (!Number.isInteger(v) || v <= 0) {
throw new Error(`targetVersion must be a positive integer, got ${targetVersion}`);
} Type guard
function isPositiveInteger(v) {
return typeof v === 'number' && Number.isInteger(v) && v > 0;
} Try / catch
try {
await rollbackTo(skillPath, targetVersion);
} catch (err) {
if (err.message.startsWith('Invalid target version')) {
console.error(`Bad version '${targetVersion}' — use a 1-based integer`);
} else throw err;
} Prevention
- Parse user input with Number.parseInt before calling, never pass raw strings
- Remember versions are 1-based; convert 0-based indexes with idx + 1
- Keep argument order (skillPath, targetVersion, options) — don't shift args
- Add CLI validation that rejects non-numeric version input
When it happens
Trigger: Calling rollbackTo(skillPath, 'latest') or rollbackTo(skillPath, 0) or rollbackTo(skillPath, -1) or passing a float like 2.5; also passing a version object instead of a number.
Common situations: UI code forwarding user text straight into targetVersion; computing a version from an off-by-one index (0-based vs 1-based); accidentally passing an options object as the second argument after a signature change.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- -32602
- a claim token is required
- a confirmed nonempty coordinate is required
- a generated candidate cannot claim original-source identity
- A managed install plan with operations is required.
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/d40bf7eabaacd74f.
Report an issue: GitHub.
Appendix: 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 8321021c54)