{"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":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/harness-capabilities.js","lineNumber":331,"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":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/harness-capabilities.js#L313-L349","documentation":"normalizeHarnessSelection validates a guided harness selection string/array and throws when it is empty or every token normalizes to 'none' — the interactive installer requires at least one harness to configure. It is called by installer prompts and helpers (askHarnesses, normalizedHarnesses, etc.).","triggerScenarios":"Invoking the guided installer with an empty selection, pressing enter with no input at the harness prompt, passing 'none' (or only whitespace/commas), or programmatically calling selectedHarnessIds([]) / normalizeHarnessSelection('').","commonSituations":"Users running the non-interactive installer with --harnesses \"\" or --harnesses none; scripting the installer where a variable holding the harness list is unset; CI configs where the harness flag was dropped during refactoring.","solutions":["Provide at least one harness token, e.g. --harnesses claude-code or --harnesses all.","If you truly want no harness integration, skip the guided harness step rather than passing 'none'.","Check the scripted invocation for an unset/empty variable feeding the selection.","Use the interactive prompt and select the harnesses you use."],"exampleFix":"// before\nconst selection = process.env.HARNESSES || ''; // empty -> throws\nconst ids = normalizeHarnessSelection(selection);\n\n// after\nconst selection = process.env.HARNESSES || 'claude-code';\nconst ids = normalizeHarnessSelection(selection);","handlingStrategy":"validation","validationCode":"const tokens = String(selection || '').split(',').map(t => t.trim()).filter(Boolean);\nif (tokens.length === 0 || tokens.every(t => t.toLowerCase() === 'none')) {\n  throw new Error('Provide at least one harness (e.g. claude-code) or \"all\"');\n}","typeGuard":"function hasHarnessSelection(sel) {\n  const tokens = Array.isArray(sel) ? sel : String(sel || '').split(',');\n  return tokens.some(t => String(t).trim() && String(t).trim().toLowerCase() !== 'none');\n}","tryCatchPattern":"try {\n  const ids = normalizeHarnessSelection(selection);\n} catch (e) {\n  if (/At least one guided harness/.test(e.message)) {\n    console.error('Re-run with --harnesses <name> or --harnesses all');\n    process.exitCode = 2;\n    return;\n  }\n  throw e;\n}","preventionTips":["Default scripted invocations to a concrete harness instead of an empty string.","Validate the harnesses flag at CLI-parse time with a clear usage message.","Skip the guided harness step entirely when no integration is wanted, rather than passing 'none'."],"tags":["cli","validation","installer","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}