affaan-m/ECC · error · Error
skill execution payload must be an object
Error message
skill execution payload must be an object
What it means
normalizeExecutionRecord() validates the payload handed to record() before persisting a skill execution entry. The payload must be a plain object: null, undefined, arrays, and primitives are all rejected with 'skill execution payload must be an object'. This fails fast so downstream field extraction (skill_id, skill_version, etc.) operates on real object input.
Solutions
- Pass a single execution-record object to record(), e.g. record({ skill_id, skill_version, task_description, outcome }).
- If you have multiple records, call record() once per object in a loop/map instead of passing an array.
- Check the call site for a missing argument or failed JSON.parse returning undefined.
- Guard with a type check before calling: if (payload && !Array.isArray(payload)) record(payload).
Example fix
// before
tracker.record(JSON.parse(batchFile)); // array of records
// after
for (const rec of JSON.parse(batchFile)) {
tracker.record(rec);
} Defensive patterns
Strategy: type-guard
Validate before calling
function isRecordPayload(input) {
return input !== null && typeof input === 'object' && !Array.isArray(input);
}
if (!isRecordPayload(payload)) {
throw new TypeError('record() expects a single execution object, not an array or primitive');
}
tracker.record(payload); Type guard
function isPlainObject(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
} Try / catch
try {
tracker.record(payload);
} catch (err) {
if (err.message === 'skill execution payload must be an object') {
console.error('record() received non-object payload; check for missing arg or array batch');
return;
}
throw err;
} Prevention
- Never pass arrays to record(); loop over batches one record at a time.
- Check JSON.parse results for undefined/null before passing them on.
- Wrap record() in a thin adapter that validates the payload shape first.
When it happens
Trigger: Calling record(null), record(undefined), record([...] ) with an array of records, or record('skill run') — any input where !input || typeof input !== 'object' || Array.isArray(input) is true.
Common situations: Passing an array of execution records to record() instead of looping; spreading arguments so the payload becomes undefined; a build step that dropped the object argument; JSON.parse failures silently yielding undefined then passed through.
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
- must be a number
- outcome must be one of success, failure, or partial
- recorded_at must be an ISO timestamp
- skill_id is required
- skill_version is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/58ddc9462a3a6eb0.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/skill-evolution/tracker.js:47
return path.join(resolveHomeDir(options.homeDir), '.claude', 'state', 'skill-runs.jsonl');
}
function toNullableNumber(value, fieldName) {
if (value === null || typeof value === 'undefined') {
return null;
}
const numericValue = Number(value);
if (!Number.isFinite(numericValue)) {
throw new Error(`${fieldName} must be a number`);
}
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) {View on GitHub (pinned to 8321021c54)