{"record":{"id":"bd47b0a378d55960","repo":"ruvnet/ruflo","slug":"invalid-worker-spec-spec-missing-prompt-af","errorCode":null,"errorMessage":"Invalid --worker spec \"${spec}\". Missing prompt after \"<platform>:<role>:\".","messagePattern":"Invalid --worker spec \"(.+?)\"\\. Missing prompt after \"<platform>:<role>:\"\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/codex/src/dual-mode/cli.ts","lineNumber":277,"sourceCode":" * Parse `--worker \"<platform>:<role>:<prompt>\"` specs into WorkerConfig[].\n * Splits on the first two `:` so the prompt may itself contain colons.\n * Workers chain sequentially (each depends on the previous) unless `parallel`.\n */\nexport function parseWorkerSpecs(specs: string[], parallel: boolean): WorkerConfig[] {\n  const usedIds = new Set<string>();\n  const workers: WorkerConfig[] = [];\n\n  specs.forEach((spec, index) => {\n    const firstColon = spec.indexOf(':');\n    const secondColon = firstColon >= 0 ? spec.indexOf(':', firstColon + 1) : -1;\n    if (firstColon < 0 || secondColon < 0) {\n      throw new Error(`Invalid --worker spec \"${spec}\". Expected \"<platform>:<role>:<prompt>\" (platform = claude|codex).`);\n    }\n    const platformRaw = spec.slice(0, firstColon).trim().toLowerCase();\n    const role = spec.slice(firstColon + 1, secondColon).trim() || `worker-${index + 1}`;\n    const prompt = spec.slice(secondColon + 1).trim();\n    if (!prompt) {\n      throw new Error(`Invalid --worker spec \"${spec}\". Missing prompt after \"<platform>:<role>:\".`);\n    }\n    if (platformRaw !== 'claude' && platformRaw !== 'codex') {\n      throw new Error(`Invalid platform \"${platformRaw}\" in --worker spec \"${spec}\". Use \"claude\" or \"codex\".`);\n    }\n    const platform: 'claude' | 'codex' = platformRaw;\n\n    // Derive a unique id from the role.\n    const base = role.replace(/\\s+/g, '-');\n    let id = base;\n    let suffix = 2;\n    while (usedIds.has(id)) { id = `${base}-${suffix++}`; }\n    usedIds.add(id);\n\n    const worker: WorkerConfig = { id, platform, role, prompt };\n    const prev = workers[workers.length - 1];\n    if (!parallel && prev) {\n      worker.dependsOn = [prev.id];\n    }","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/codex/src/dual-mode/cli.ts#L259-L295","documentation":"parseWorkerSpecs() variant thrown when the spec has the two structural colons but the text after the second colon trims to an empty string — the prompt segment is missing even though the shape looks right. The role may default (worker-N) when blank, but the prompt is mandatory because it is the actual task instruction handed to the spawned claude/codex process.","triggerScenarios":"(1) `--worker \"codex:reviewer:\"` — trailing colon with nothing after it; (2) a spec whose third segment is only whitespace, e.g. \"claude:impl:   \"; (3) templates or scripts that concatenate an empty prompt variable into the spec string.","commonSituations":"Script-built worker specs where $PROMPT was unset; shell history edits that truncated the tail; copying a spec and deleting the prompt to 'fill in later' then running it.","solutions":["Append a real task description after the second colon: `--worker \"codex:reviewer:Check error handling in src/api\"`.","If building specs programmatically, assert the prompt variable is non-empty before concatenating.","Trim leading/trailing whitespace so accidental blank prompts are caught by your own validation with a clearer message.","Quote the whole spec so trailing spaces/colons survive the shell intact."],"exampleFix":"# before\n$ dual-mode collaborate --worker \"codex:reviewer:\"\nError: Invalid --worker spec \"codex:reviewer:\". Missing prompt after \"<platform>:<role>:\".\n\n# after\n$ dual-mode collaborate --worker \"codex:reviewer:Audit the retry logic in uploadToGCS\"","handlingStrategy":"validation","validationCode":"const specs = rawSpecs.map(s => s.trim());\nconst empties = specs.filter(s => {\n  const second = s.indexOf(':', s.indexOf(':') + 1);\n  return second < 0 || s.slice(second + 1).trim() === '';\n});\nif (empties.length) {\n  throw new Error(`worker specs missing a prompt: ${empties.join(' | ')}`);\n}\nconst workers = parseWorkerSpecs(specs, parallel);","typeGuard":null,"tryCatchPattern":"try {\n  const workers = parseWorkerSpecs(specs, parallel);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Missing prompt after')) {\n    throw new Error(`every worker needs a task description after \"<platform>:<role>:\" — got: ${specs.join(', ')}`);\n  }\n  throw err;\n}","preventionTips":["When building specs programmatically, assert the prompt variable is non-empty before string concatenation","Trim inputs so whitespace-only prompts fail your own validation with a clearer message than the library's","Treat an empty prompt as a config bug in the generating script, not a runtime choice","Keep prompts actionable ('Review X for Y') — the spawned worker gets nothing else to do"],"tags":["dual-mode","cli-arguments","parsing","empty-input"],"backgroundTag":"invalid-cli-argument-format","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}