affaan-m/ECC · error
Unknown evolution log type: ${logType}
Error message
Unknown evolution log type: ${logType} What it means
Thrown by getEvolutionLogPath() when the requested logType is not in the EVOLUTION_LOG_TYPES allowlist (['observations','inspections','amendments']). The library writes one JSONL file per log type inside the skill's .evolution directory, so an unknown type would create a stray file and break tooling that reads those logs. The check is a hard gate before any path is computed.
Source
Thrown at scripts/lib/skill-evolution/versioning.js:52
const skillFilePath = getSkillFilePath(skillPath);
if (!fs.existsSync(skillFilePath)) {
throw new Error(`Skill file not found: ${skillFilePath}`);
}
return skillFilePath;
}
function getVersionsDir(skillPath) {
return path.join(normalizeSkillDir(skillPath), VERSION_DIRECTORY_NAME);
}
function getEvolutionDir(skillPath) {
return path.join(normalizeSkillDir(skillPath), EVOLUTION_DIRECTORY_NAME);
}
function getEvolutionLogPath(skillPath, logType) {
if (!EVOLUTION_LOG_TYPES.includes(logType)) {
throw new Error(`Unknown evolution log type: ${logType}`);
}
return path.join(getEvolutionDir(skillPath), `${logType}.jsonl`);
}
function ensureSkillVersioning(skillPath) {
ensureSkillExists(skillPath);
const versionsDir = getVersionsDir(skillPath);
const evolutionDir = getEvolutionDir(skillPath);
ensureDir(versionsDir);
ensureDir(evolutionDir);
for (const logType of EVOLUTION_LOG_TYPES) {
const logPath = getEvolutionLogPath(skillPath, logType);
if (!fs.existsSync(logPath)) {
fs.writeFileSync(logPath, '', 'utf8');View on GitHub (pinned to 01e15490f0)
Solutions
- Use one of the three allowed values exactly: 'observations', 'inspections', or 'amendments'.
- Import EVOLUTION_LOG_TYPES from the module and validate or iterate against it instead of hard-coding the string.
- If you genuinely need a new log type, add it to EVOLUTION_LOG_TYPES in scripts/lib/skill-evolution/versioning.js and update consumers — do not catch and ignore the error.
- Check for trailing whitespace or wrong casing in the value you pass; the comparison is case-sensitive.
Example fix
// before
appendEvolutionRecord(skillPath, 'observation', record); // singular -> Unknown evolution log type
// after
const { EVOLUTION_LOG_TYPES, appendEvolutionRecord } = require('scripts/lib/skill-evolution/versioning');
const logType = EVOLUTION_LOG_TYPES.includes(inputType) ? inputType : 'observations';
appendEvolutionRecord(skillPath, logType, record); Defensive patterns
Strategy: validation
Validate before calling
const { EVOLUTION_LOG_TYPES } = require('scripts/lib/skill-evolution/versioning');
function safeLogType(value, fallback = 'observations') {
return EVOLUTION_LOG_TYPES.includes(value) ? value : fallback;
}
appendEvolutionRecord(skillPath, safeLogType(inputType), record); Type guard
const { EVOLUTION_LOG_TYPES } = require('scripts/lib/skill-evolution/versioning');
function isEvolutionLogType(value) {
return typeof value === 'string' && EVOLUTION_LOG_TYPES.includes(value);
} Try / catch
try {
appendEvolutionRecord(skillPath, logType, record);
} catch (error) {
if (/Unknown evolution log type/.test(error.message)) {
// fall back to a known log type or surface a config error to the caller
appendEvolutionRecord(skillPath, 'observations', record);
return;
}
throw error;
} Prevention
- Never hard-code log type strings — import EVOLUTION_LOG_TYPES and index into it.
- Treat the allowlist as part of the public API: a new log type requires a library bump.
- Validate at the edge (CLI parser, config loader) so a bad value is rejected with context.
- Document the three allowed values wherever you accept a logType parameter.
When it happens
Trigger: Calling appendEvolutionRecord(skillPath, 'audit', record) or getEvolutionLog(skillPath, 'feedback'); passing a plural/singular variant like 'observation' instead of 'observations'; passing a custom log type the caller assumed was registered; passing undefined/null/logType that fails Array.prototype.includes.
Common situations: Caller hard-codes a log type string and the API later adds a new bucket the caller does not know about; copy-paste from documentation that uses a different spelling; downstream tool tries to write 'reviews' or 'tests' logs that are not part of the schema.
Related errors
- Invalid mode "${mode}". Allowed modes: ${allowedModes.join('
- Project memory .gitignore does not contain the required fail
- Choose at least one guided harness: Claude, Codex, or Kimi.
- Claude scope and hook options require Claude to be selected.
- The managed install profile requires Kimi to be selected.
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/5b1e6ed584f9e43c.
Report an issue: GitHub.