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

  1. Check the exact spelling of the lineage argument against the values in envelope.LINEAGES (import or log them).
  2. Use one of the documented lineages verbatim (lowercase, exact string).
  3. If a genuinely new lineage is needed, add it to LINEAGES in the envelope module and update projection consumers — do not pass arbitrary strings.
  4. 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

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


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)