affaan-m/ECC · error · Error
outcome must be one of success, failure, or partial
Error message
outcome must be one of success, failure, or partial
What it means
normalizeExecutionRecord validates the outcome field against VALID_OUTCOMES and throws 'outcome must be one of success, failure, or partial' when the value is outside that set. The tracker only supports three canonical outcomes so downstream analysis can group execution records reliably.
Solutions
- Use exactly one of 'success', 'failure', or 'partial' (lowercase) as the outcome value.
- Normalize upstream statuses before calling: map runner results onto the three allowed values.
- If you need additional outcome categories, extend VALID_OUTCOMES in tracker.js and its consumers deliberately, not ad hoc.
Example fix
// before
tracker.record({ skillId, skillVersion, taskDescription, outcome: 'passed' });
// after
const outcome = testsFailed ? 'failure' : fixesApplied ? 'partial' : 'success';
tracker.record({ skillId, skillVersion, taskDescription, outcome }); Defensive patterns
Strategy: validation
Validate before calling
const VALID_OUTCOMES = new Set(['success', 'failure', 'partial']);
function assertOutcome(outcome) {
if (!VALID_OUTCOMES.has(outcome)) {
throw new Error(`outcome must be one of success, failure, or partial (got ${JSON.stringify(outcome)})`);
}
} Type guard
function isValidOutcome(v) {
return v === 'success' || v === 'failure' || v === 'partial';
} Try / catch
try {
tracker.record(record);
} catch (err) {
if (err.message.startsWith('outcome must be one of')) {
console.error(`Invalid outcome "${record.outcome}"; use success | failure | partial.`);
process.exitCode = 1;
return;
}
throw err;
} Prevention
- Map external status vocabularies (passed/failed, ok/error) onto the three tracker outcomes at the boundary.
- Lowercase and trim the outcome before recording to avoid case mismatches.
- Keep a single shared constant/list of allowed outcomes in caller code, mirroring the library's VALID_OUTCOMES.
When it happens
Trigger: Calling record() with outcome values like 'ok', 'SUCCESS', 'passed', 'error', true/false, or omitting outcome entirely (undefined).
Common situations: Mapping another tool's status vocabulary onto the tracker without translating (e.g. 'passed'/'failed' from a test runner); case-sensitivity mistakes ('Success'); shell scripts quoting or casing the flag incorrectly; newer callers assuming more outcome categories exist.
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
- user_feedback must be accepted, corrected, rejected, or null
- 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/ad97df5595b8e0c7.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/skill-evolution/tracker.js:70
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');
}
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'),View on GitHub (pinned to 8321021c54)