affaan-m/ECC · error · Error

skill_id is required

Error message

skill_id is required

What it means

normalizeExecutionRecord() requires every execution record to identify which skill ran. skill_id is read from input.skill_id or input.skillId (camelCase accepted); if it is missing, not a string, or an empty/whitespace-only string, the function throws 'skill_id is required'. skill_id is the primary key for correlating executions with a skill, so it cannot be defaulted.

Solutions

  1. Add a non-empty skill_id string to the payload: record({ skill_id: 'my-skill', ... }).
  2. If your field is named differently (e.g. 'name'), rename it to skill_id (or skillId) before calling.
  3. Trim and validate the value upstream; reject empty strings before they reach the tracker.
  4. Coerce numeric IDs to strings (String(id)) if your source stores them as numbers.

Example fix

// before
record({ skill_version: '1.2.0', task_description: 'lint repo', outcome: 'success' });

// after
record({ skill_id: 'lint-repo', skill_version: '1.2.0', task_description: 'lint repo', outcome: 'success' });
Defensive patterns

Strategy: validation

Validate before calling

function assertSkillId(payload) {
  const id = payload?.skill_id ?? payload?.skillId;
  if (typeof id !== 'string' || id.trim().length === 0) {
    throw new TypeError('execution payload missing non-empty skill_id');
  }
  return id.trim();
}

Type guard

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

Try / catch

try {
  tracker.record(payload);
} catch (err) {
  if (err.message === 'skill_id is required') {
    console.error('Execution payload missing skill_id:', JSON.stringify(Object.keys(payload)));
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: record({ skill_version: '1.0.0', task_description: 'x', outcome: 'success' }) with no skill_id/skillId key; passing skill_id: '' or skill_id: ' '; passing a non-string like skill_id: 42.

Common situations: Renaming the field in your telemetry pipeline to 'skill' or 'name' so skill_id is never set; building payloads programmatically and forgetting the id; trimming user input that was empty; using snake_case/camelCase inconsistently when both are undefined.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/bb047328bfb73a9a. Report an issue: GitHub.

Appendix: 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 8321021c54)