affaan-m/ECC · error · CapsuleError
capsule.bad_lineage
capsule.bad_lineage
Error message
unknown lineage ${lineage} What it means
append() validates its lineage argument against envelope.LINEAGES (the five documented lineages, e.g. capsule/fix/feat/task families). Any lineage string not in that allowlist is rejected with code 'capsule.bad_lineage'. Lineages are a closed enum because the projection groups entries by lineage.
Solutions
- Check the exact spelling of the lineage argument against the values in envelope.LINEAGES (import or log them).
- Use one of the documented lineages verbatim (lowercase, exact string).
- If a genuinely new lineage is needed, add it to LINEAGES in the envelope module and update projection consumers — do not pass arbitrary strings.
- Guard call sites that compute lineage dynamically with a check against LINEAGES before calling append().
Example fix
// before
await capsule.append('Fix', 'note', { msg }); // throws capsule.bad_lineage
// after
if (!envelope.LINEAGES.includes('fix')) throw new Error('unsupported lineage');
await capsule.append('fix', 'note', { msg }); Defensive patterns
Strategy: validation
Validate before calling
import { LINEAGES } from './envelope.js';
if (!LINEAGES.includes(lineage)) throw new Error(`lineage must be one of: ${LINEAGES.join(', ')}`); Type guard
function isValidLineage(lineage) {
return typeof lineage === 'string' && envelope.LINEAGES.includes(lineage);
} Try / catch
try {
await capsule.append(lineage, kind, payload);
} catch (e) {
if (e instanceof CapsuleError && e.code === 'capsule.bad_lineage') {
throw new Error(`${e.message}; valid lineages: ${envelope.LINEAGES.join(', ')}`);
}
throw e;
} Prevention
- Import lineage names from envelope.LINEAGES instead of inlining strings.
- Wrap append() in a small helper that validates lineage once.
- Add a unit test per call site asserting the lineage constant is in LINEAGES.
- Never derive lineage from raw user/CLI input without allowlist checking.
When it happens
Trigger: Calling append() with a misspelled lineage ('Fix', 'fixes', 'bugfix'), a dynamic/interpolated lineage value, or a lineage from an older/newer schema version that this library build does not recognize.
Common situations: Typo in the lineage name; wrapping lineage in a variable whose value came from CLI args or config; refactoring that renamed a lineage without updating all append() call sites.
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
- --all-harnesses and --harness are mutually exclusive.
- artifact has invalid modality binding
- assigneeKind must be 'agent' or 'human'.
- Choose exactly one memory body source: --stdin or…
- Conflict messages require at least one --file
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/870db6f7ba6cf27a.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/eval-harness/capsule.js:183
const capsule = new Capsule(resolved, state.meta, options);
if (state.entries.length > 0) {
const last = state.entries[state.entries.length - 1];
capsule.lastHash = last.entry_hash;
capsule.nextSeq = last.seq + 1;
}
return capsule;
}
/**
* Serialize cooperating appenders and validate current disk state under lock.
* A partial I/O failure is preserved for diagnosis, never silently rolled back.
*/
append(lineage, kind, payload = {}, options = {}) {
return withAppendLock(this.dir, () => {
const state = readCapsule(this.dir);
if (!state.ok) throw new CapsuleError(state.code, state.reason, { failed_at: state.failed_at });
if (!envelope.LINEAGES.includes(lineage)) {
throw new CapsuleError('capsule.bad_lineage', `unknown lineage ${lineage}`);
}
const effectClass = options.effect_class || 'SE0';
const { payload: clean, dropped, findings, errors: payloadErrors } = envelope.redactPayload(payload, options);
if (payloadErrors.length > 0) {
throw new CapsuleError('capsule.payload_invalid', payloadErrors.join('; '));
}
if (findings.length > 0) {
throw new CapsuleError('capsule.secret_canary', `payload tripped secret canary ${findings[0].canary} at ${findings[0].path}`, { findings });
}
if (dropped.length > 0 && options.strict !== false) {
throw new CapsuleError('capsule.payload_denied', `payload keys not allowlisted: ${dropped.join(', ')}`, { dropped });
}
const body = {
schema: envelope.SCHEMA_VERSION,
run_id: state.meta.run_id,
capsule_id: state.meta.capsule_id,
seq: state.entries.length,
ts: nowIso(this.clock),View on GitHub (pinned to 8321021c54)