affaan-m/ECC · error · Error

At least one guided harness must be selected

Error message

At least one guided harness must be selected

What it means

Thrown by normalizeHarnessSelection() in scripts/lib/harness-capabilities.js when the input contains zero usable tokens, or every token normalizes to 'none'. The guided flow requires at least one harness to install for.

Source

Thrown at scripts/lib/harness-capabilities.js:317

function getHarnessCapability(value) {
  if (typeof value !== 'string' || value.trim() === '') {
    return null;
  }

  return LOOKUP.get(normalizeLookupToken(value)) || null;
}

function tokenizeSelection(selection) {
  const values = Array.isArray(selection) ? selection : [selection];
  return values.flatMap(value => (
    typeof value === 'string' ? value.split(',') : []
  )).map(value => value.trim()).filter(Boolean);
}

function normalizeHarnessSelection(selection) {
  const tokens = tokenizeSelection(selection);
  if (tokens.length === 0 || tokens.every(token => normalizeLookupToken(token) === 'none')) {
    throw new Error('At least one guided harness must be selected');
  }

  const allTokens = tokens.filter(token => ['all', '*'].includes(normalizeLookupToken(token)));
  const explicitTokens = tokens.filter(token => !['all', '*'].includes(normalizeLookupToken(token)));
  if (allTokens.length > 0 && explicitTokens.length > 0) {
    throw new Error('The all/* harness selection cannot be combined with other selections');
  }
  if (allTokens.length > 0) {
    return GUIDED_HARNESS_IDS.slice();
  }

  const selected = new Set();
  for (const token of tokens) {
    const normalizedToken = normalizeLookupToken(token);
    const menuIndex = /^\d+$/.test(normalizedToken) ? Number(normalizedToken) - 1 : -1;
    const harness = menuIndex >= 0
      ? listGuidedHarnesses()[menuIndex] || null
      : getHarnessCapability(token);

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Pass at least one guided harness id, e.g. 'claude', 'codex', or 'kimi' (the GUIDED_HARNESS_IDS set).
  2. Use 'all' or '*' to select every guided harness.
  3. If 'none' is intentional, handle it upstream before calling normalizeHarnessSelection rather than passing it through.

Example fix

// before
const sel = normalizeHarnessSelection(process.env.HARNESS_SELECTION);

// after
const raw = process.env.HARNESS_SELECTION || 'claude';
if (raw.trim().toLowerCase() === 'none') {
  console.log('Skipping harness install');
  return [];
}
const sel = normalizeHarnessSelection(raw);
Defensive patterns

Strategy: validation

Validate before calling

function hasSelection(sel) {
  const tokens = (Array.isArray(sel) ? sel : [sel])
    .flatMap(v => typeof v === 'string' ? v.split(',') : [])
    .map(v => v.trim()).filter(Boolean);
  return tokens.length > 0 && !tokens.every(t => t.trim().toLowerCase() === 'none');
}
if (!hasSelection(process.env.HARNESS_SELECTION)) {
  console.error('No harness selected; defaulting to claude');
}

Try / catch

try {
  return normalizeHarnessSelection(sel);
} catch (err) {
  if (/At least one guided harness/.test(err.message)) {
    return normalizeHarnessSelection('claude');
  }
  throw err;
}

Prevention

When it happens

Trigger: normalizeHarnessSelection(undefined), normalizeHarnessSelection(''), normalizeHarnessSelection([]), normalizeHarnessSelection('none'), or normalizeHarnessSelection('none,none'). After tokenizeSelection the array is empty or all tokens normalize to 'none'.

Common situations: CLI flag --harness not provided and defaulted to empty; user typed '--harness none' meaning skip; config file has harnesses: [] or harnesses: 'none'; env var HARNESS_SELECTION unset.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/1e6c113812c6c39c. Report an issue: GitHub.