{"record":{"id":"876502910b11e31c","repo":"ruvnet/ruflo","slug":"dual-mode-config-must-export-a-workers-array-ab","errorCode":null,"errorMessage":"Dual-mode config must export a workers array: ${absolute}","messagePattern":"Dual-mode config must export a workers array: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/dual-mode/cli.ts","lineNumber":195,"sourceCode":"      console.log();\n\n      printResults(result);\n    });\n}\n\nexport async function loadWorkerConfig(\n  configPath: string,\n  cwd = process.cwd(),\n): Promise<{ workers: WorkerConfig[]; taskContext?: string }> {\n  const absolute = path.resolve(cwd, configPath);\n  const loaded = path.extname(absolute).toLowerCase() === '.json'\n    ? JSON.parse(await readFile(absolute, 'utf8'))\n    : await import(pathToFileURL(absolute).href);\n  const config = loaded.default && typeof loaded.default === 'object'\n    ? loaded.default\n    : loaded;\n  if (!Array.isArray(config.workers)) {\n    throw new Error(`Dual-mode config must export a workers array: ${absolute}`);\n  }\n  return {\n    workers: config.workers,\n    ...(typeof config.taskContext === 'string' ? { taskContext: config.taskContext } : {}),\n  };\n}\n\n/**\n * List available templates\n */\nfunction createTemplateCommand(): Command {\n  return new Command('templates')\n    .description('List available collaboration templates')\n    .action(() => {\n      console.log(chalk.bold('\\nAvailable Collaboration Templates:\\n'));\n\n      console.log(chalk.cyan('feature') + ' - Feature Development Swarm');\n      console.log('  Pipeline: architect → coder → tester → reviewer');","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/dual-mode/cli.ts#L177-L213","documentation":"loadWorkerConfig() reads a worker config from a .json file (JSON.parse) or imports a .js/.mjs/.ts module (dynamic import), unwraps a default export if present, and requires the resulting object to have a `workers` array. Any config whose effective export lacks an array-typed workers key — missing key, typoed key, wrong type, or a module exporting a function/class instead of a plain object — is rejected with the absolute path of the offending file.","triggerScenarios":"(1) A config module that exports only `taskContext` or metadata and forgets workers; (2) key spelled `worker:` instead of `workers:`; (3) `export default () => ({ workers })` — the default is a function, not an object; (4) a .json file whose top level is an array or where workers is an object rather than an array; (5) editing the wrong file — the error prints the resolved absolute path to confirm.","commonSituations":"First-time dual-mode configs copied from partial examples; ESM/CJS interop where module.exports vs export default produce different shapes; refactors that renamed the key; JSON hand-edits introducing type changes.","solutions":["Make the file export (default or namespace) an object containing a workers array: `export default { workers: [{ id: 'impl', platform: 'claude', role: 'implementer', prompt: 'Implement X' }] }`.","Compare against the absolute path in the error message to be sure you edited the file that is actually loaded (path.resolve(cwd, configPath)).","For JSON configs, verify with a quick check that `Array.isArray(JSON.parse(text).workers)` is true.","Optionally include `taskContext` as a string — it is the only other accepted key."],"exampleFix":"// before — workers.config.ts\nexport default { worker: [ { id: 'impl', platform: 'claude', role: 'impl', prompt: 'Do X' } ] };\n// → Error: Dual-mode config must export a workers array: /repo/workers.config.ts\n\n// after\nexport default {\n  workers: [ { id: 'impl', platform: 'claude', role: 'implementer', prompt: 'Do X' } ],\n  taskContext: 'Refactor the auth module',\n};","handlingStrategy":"type-guard","validationCode":"const loaded = await import(pathToFileURL(absolute).href);\nconst candidate = loaded.default && typeof loaded.default === 'object' ? loaded.default : loaded;\nif (!isWorkerConfigArray((candidate as { workers?: unknown }).workers)) {\n  throw new Error(`${absolute} must export { workers: WorkerConfig[] }`);\n}","typeGuard":"function isWorkerConfigArray(v: unknown): v is Array<{ id: string; platform: string; role: string; prompt: string }> {\n  return Array.isArray(v) && v.length > 0 && v.every(w =>\n    typeof w === 'object' && w !== null &&\n    typeof (w as { id?: unknown }).id === 'string' &&\n    ((w as { platform?: unknown }).platform === 'claude' || (w as { platform?: unknown }).platform === 'codex') &&\n    typeof (w as { prompt?: unknown }).prompt === 'string');\n}","tryCatchPattern":"try {\n  const { workers, taskContext } = await loadWorkerConfig(configPath, cwd);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Dual-mode config must export a workers array:')) {\n    throw new Error(`config shape wrong — the file at the printed path needs export default { workers: [...] }`);\n  }\n  throw err; // import/JSON.parse failures surface as different errors\n}","preventionTips":["Standardize on `export default { workers: [...] }` for .ts/.js configs — it survives the default-unwrap logic in both ESM and CJS interop","The error prints the resolved absolute path — always diff it against the file you edited before debugging further","Validate the shape with the type guard in CI when configs are generated","Prefer JSON configs for machine-generated worker lists; they fail loudly and early"],"tags":["dual-mode","config","module-import","validation","esm"],"backgroundTag":"config-missing-required-field","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}