affaan-m/ECC · error

skill_id is required

Error message

skill_id is required

What it means

Thrown by normalizeExecutionRecord when skill_id (or the camelCase alias skillId) is not a non-empty trimmed string. It is the first required-field check after the object guard.

Source

Thrown at scripts/lib/skill-evolution/tracker.js:58

  }

  return numericValue;
}

function normalizeExecutionRecord(input, options = {}) {
  if (!input || typeof input !== 'object' || Array.isArray(input)) {
    throw new Error('skill execution payload must be an object');
  }

  const skillId = input.skill_id || input.skillId;
  const skillVersion = input.skill_version || input.skillVersion;
  const taskDescription = input.task_description || input.task_attempted || input.taskAttempted;
  const outcome = input.outcome;
  const recordedAt = input.recorded_at || options.now || new Date().toISOString();
  const userFeedback = input.user_feedback || input.userFeedback || null;

  if (typeof skillId !== 'string' || skillId.trim().length === 0) {
    throw new Error('skill_id is required');
  }

  if (typeof skillVersion !== 'string' || skillVersion.trim().length === 0) {
    throw new Error('skill_version is required');
  }

  if (typeof taskDescription !== 'string' || taskDescription.trim().length === 0) {
    throw new Error('task_description is required');
  }

  if (!VALID_OUTCOMES.has(outcome)) {
    throw new Error('outcome must be one of success, failure, or partial');
  }

  if (userFeedback !== null && !VALID_FEEDBACK.has(userFeedback)) {
    throw new Error('user_feedback must be accepted, corrected, rejected, or null');
  }

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Provide skill_id as a non-empty string (skillId alias also accepted).
  2. Resolve the id from the skill's frontmatter `name` before recording.
  3. Validate the field exists and trims to non-empty.

Example fix

// before
recordSkillRun({ skill_version: '1.0.0', task_description: 'x', outcome: 'success' });

// after
recordSkillRun({ skill_id: skill.frontmatter.name, skill_version, task_description, outcome });
Defensive patterns

Strategy: validation

Validate before calling

function requireSkillId(input) {
  const id = input.skill_id || input.skillId;
  if (typeof id !== 'string' || id.trim().length === 0) {
    throw new TypeError('skill_id missing');
  }
  return id;
}

Type guard

function hasSkillId(input) {
  const id = input?.skill_id || input?.skillId;
  return typeof id === 'string' && id.trim().length > 0;
}

Try / catch

try {
  recordSkillRun(input);
} catch (err) {
  if (/skill_id is required/.test(err.message)) { input.skill_id = fallbackId; recordSkillRun(input); }
  else throw err;
}

Prevention

When it happens

Trigger: recordSkillRun({ skill_version, task_description, outcome }) with no skill_id; skill_id: '' or ' '; skill_id: 123 (number, not string).

Common situations: Recording a run before the skill id is resolved; field-name mismatch (e.g. sending `id` or `skill` instead of `skill_id`); trimming reveals whitespace-only value.

Related errors


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