affaan-m/ECC · error · Error
user_feedback must be accepted, corrected, rejected, or null
Error message
user_feedback must be accepted, corrected, rejected, or null
What it means
normalizeExecutionRecord validates userFeedback against VALID_FEEDBACK and throws 'user_feedback must be accepted, corrected, rejected, or null' when a non-null value outside that set is supplied. userFeedback is optional but must be either null/omitted or exactly one of the three recognized feedback values.
Solutions
- Pass one of 'accepted', 'corrected', 'rejected' (lowercase) or explicitly null.
- Translate UI-level feedback (booleans, ratings) into the three enum values before recording.
- Omit the field or pass null when no user feedback was collected, instead of inventing a placeholder value.
Example fix
// before
tracker.record({ skillId, skillVersion, taskDescription, outcome, userFeedback: 'yes' });
// after
const userFeedback = userReverted ? 'rejected' : userEdited ? 'corrected' : userApproved ? 'accepted' : null;
tracker.record({ skillId, skillVersion, taskDescription, outcome, userFeedback }); Defensive patterns
Strategy: validation
Validate before calling
const VALID_FEEDBACK = new Set(['accepted', 'corrected', 'rejected']);
function assertUserFeedback(userFeedback) {
if (userFeedback !== null && !VALID_FEEDBACK.has(userFeedback)) {
throw new Error('user_feedback must be accepted, corrected, rejected, or null');
}
} Type guard
function isValidUserFeedback(v) {
return v === null || v === 'accepted' || v === 'corrected' || v === 'rejected';
} Try / catch
try {
tracker.record(record);
} catch (err) {
if (err.message.startsWith('user_feedback must be')) {
console.error(`Invalid user_feedback "${record.userFeedback}"; use accepted | corrected | rejected | null.`);
process.exitCode = 1;
return;
}
throw err;
} Prevention
- Treat absence of feedback as null, never as an empty string or placeholder word.
- Convert UI signals (booleans, ratings, free text) into the three enum values at the collection point.
- Reuse one mapping function for feedback translation so all callers produce identical enum values.
When it happens
Trigger: Calling record() with userFeedback set to 'yes', 'good', 'ok', a boolean, an empty string, or any other value not in {'accepted','corrected','rejected'} while also not being null.
Common situations: Prompt-driven scripts capturing free-text user sentiment and passing it straight through; mapping thumbs-up/down booleans onto the field instead of choosing an enum value; typos like 'accepted!' or 'accept'; older scripts written before the feedback field existed passing an incompatible default.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- outcome must be one of success, failure, or partial
- artifact has invalid modality binding
- assigneeKind must be 'agent' or 'human'.
- CV effect has an invalid subject anchor
- explicit candidate review state required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/b1d58d80a129bd47.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/skill-evolution/tracker.js:74
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');
}
if (Number.isNaN(Date.parse(recordedAt))) {
throw new Error('recorded_at must be an ISO timestamp');
}
return {
skill_id: skillId,
skill_version: skillVersion,
task_description: taskDescription,
outcome,
failure_reason: input.failure_reason || input.failureReason || null,
tokens_used: toNullableNumber(input.tokens_used ?? input.tokensUsed, 'tokens_used'),
duration_ms: toNullableNumber(input.duration_ms ?? input.durationMs, 'duration_ms'),
user_feedback: userFeedback,
recorded_at: recordedAt,
};
}View on GitHub (pinned to 8321021c54)