{"record":{"id":"1e6c113812c6c39c","repo":"affaan-m/ECC","slug":"at-least-one-guided-harness-must-be-selected","errorCode":null,"errorMessage":"At least one guided harness must be selected","messagePattern":"At least one guided harness must be selected","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/harness-capabilities.js","lineNumber":317,"sourceCode":"function getHarnessCapability(value) {\n  if (typeof value !== 'string' || value.trim() === '') {\n    return null;\n  }\n\n  return LOOKUP.get(normalizeLookupToken(value)) || null;\n}\n\nfunction tokenizeSelection(selection) {\n  const values = Array.isArray(selection) ? selection : [selection];\n  return values.flatMap(value => (\n    typeof value === 'string' ? value.split(',') : []\n  )).map(value => value.trim()).filter(Boolean);\n}\n\nfunction normalizeHarnessSelection(selection) {\n  const tokens = tokenizeSelection(selection);\n  if (tokens.length === 0 || tokens.every(token => normalizeLookupToken(token) === 'none')) {\n    throw new Error('At least one guided harness must be selected');\n  }\n\n  const allTokens = tokens.filter(token => ['all', '*'].includes(normalizeLookupToken(token)));\n  const explicitTokens = tokens.filter(token => !['all', '*'].includes(normalizeLookupToken(token)));\n  if (allTokens.length > 0 && explicitTokens.length > 0) {\n    throw new Error('The all/* harness selection cannot be combined with other selections');\n  }\n  if (allTokens.length > 0) {\n    return GUIDED_HARNESS_IDS.slice();\n  }\n\n  const selected = new Set();\n  for (const token of tokens) {\n    const normalizedToken = normalizeLookupToken(token);\n    const menuIndex = /^\\d+$/.test(normalizedToken) ? Number(normalizedToken) - 1 : -1;\n    const harness = menuIndex >= 0\n      ? listGuidedHarnesses()[menuIndex] || null\n      : getHarnessCapability(token);","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/harness-capabilities.js#L299-L335","documentation":"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.","triggerScenarios":"normalizeHarnessSelection(undefined), normalizeHarnessSelection(''), normalizeHarnessSelection([]), normalizeHarnessSelection('none'), or normalizeHarnessSelection('none,none'). After tokenizeSelection the array is empty or all tokens normalize to 'none'.","commonSituations":"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.","solutions":["Pass at least one guided harness id, e.g. 'claude', 'codex', or 'kimi' (the GUIDED_HARNESS_IDS set).","Use 'all' or '*' to select every guided harness.","If 'none' is intentional, handle it upstream before calling normalizeHarnessSelection rather than passing it through."],"exampleFix":"// before\nconst sel = normalizeHarnessSelection(process.env.HARNESS_SELECTION);\n\n// after\nconst raw = process.env.HARNESS_SELECTION || 'claude';\nif (raw.trim().toLowerCase() === 'none') {\n  console.log('Skipping harness install');\n  return [];\n}\nconst sel = normalizeHarnessSelection(raw);","handlingStrategy":"validation","validationCode":"function hasSelection(sel) {\n  const tokens = (Array.isArray(sel) ? sel : [sel])\n    .flatMap(v => typeof v === 'string' ? v.split(',') : [])\n    .map(v => v.trim()).filter(Boolean);\n  return tokens.length > 0 && !tokens.every(t => t.trim().toLowerCase() === 'none');\n}\nif (!hasSelection(process.env.HARNESS_SELECTION)) {\n  console.error('No harness selected; defaulting to claude');\n}","typeGuard":null,"tryCatchPattern":"try {\n  return normalizeHarnessSelection(sel);\n} catch (err) {\n  if (/At least one guided harness/.test(err.message)) {\n    return normalizeHarnessSelection('claude');\n  }\n  throw err;\n}","preventionTips":["Default HARNESS_SELECTION to 'claude' (or 'all') at the CLI boundary.","Treat 'none' as 'skip' upstream instead of forwarding to normalizeHarnessSelection.","Validate selection presence in the CLI flag parser."],"tags":["validation","harness","input","install"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}