{"record":{"id":"07b611ef28c44780","repo":"ruvnet/ruflo","slug":"task-step-step-stepid-requires-config-agentid-o","errorCode":null,"errorMessage":"task step ${step.stepId} requires config.agentId or workflow.variables.defaultAgentId","messagePattern":"task step (.+?) requires config\\.agentId or workflow\\.variables\\.defaultAgentId","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/mcp-tools/workflow-tools.ts","lineNumber":355,"sourceCode":"          workflow.status = 'failed';\n          saveWorkflowStore(store);\n          break;\n        }\n\n        const step = workflow.steps[i];\n        step.status = 'running';\n        step.startedAt = new Date().toISOString();\n        const stepStart = Date.now();\n        saveWorkflowStore(store);\n\n        let stepEntry: typeof stepResults[number] = { stepId: step.stepId, type: step.type, status: 'running' };\n\n        try {\n          if (step.type === 'task') {\n            const cfg = step.config as Record<string, unknown>;\n            const agentId = (cfg.agentId as string) || (workflow.variables.defaultAgentId as string);\n            const promptTpl = (cfg.prompt as string) || step.name;\n            if (!agentId) throw new Error(`task step ${step.stepId} requires config.agentId or workflow.variables.defaultAgentId`);\n            const prompt = interp(promptTpl);\n            const result = await executeAgentTask({\n              agentId,\n              prompt,\n              systemPrompt: cfg.systemPrompt ? interp(String(cfg.systemPrompt)) : undefined,\n              maxTokens: cfg.maxTokens as number | undefined,\n              temperature: cfg.temperature as number | undefined,\n              timeoutMs: cfg.timeoutMs as number | undefined,\n            });\n            if (!result.success) throw new Error(result.error || 'agent_execute failed');\n            step.result = result;\n            workflow.variables[`${step.stepId}.output`] = result.output;\n            workflow.variables.lastStepOutput = result.output;\n            stepEntry = { stepId: step.stepId, type: 'task', status: 'completed', durationMs: result.durationMs, output: result.output };\n          } else if (step.type === 'wait') {\n            const cfg = step.config as Record<string, unknown>;\n            const ms = Math.min(Math.max(0, (cfg.ms as number) || 0), 60000);\n            await new Promise(r => setTimeout(r, ms));","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/mcp-tools/workflow-tools.ts#L337-L373","documentation":"During workflow_run, every step of type 'task' resolves its executor as step.config.agentId || workflow.variables.defaultAgentId. If both are falsy the step throws before executeAgentTask is called, marking the step failed with this message. It is a workflow-definition validation error: the workflow JSON lacks the agent binding for that step.","triggerScenarios":"Running a workflow whose task step has config without agentId while the run variables also lack defaultAgentId; passing variables: {} at start; a typo like config.agentid (case-sensitive) or nesting agentId one level too deep in the step config.","commonSituations":"Hand-written workflow JSON/YAML templates that omit per-step agent bindings; templates designed around defaultAgentId run without setting that variable; LLM-generated workflow definitions missing required fields; renaming config keys during migration from an older workflow schema.","solutions":["Add config.agentId to the failing step (stepId is named in the message), pointing at an existing agent id.","Or set workflow variables.defaultAgentId when creating/starting the run so all agent-less task steps inherit it.","Validate the workflow definition before execution: every type:'task' step must have a non-empty config.agentId or the run must receive defaultAgentId.","Check exact key names and casing — only 'agentId' (camelCase) under step.config is read."],"exampleFix":"// before\n{ steps: [{ stepId: 's1', type: 'task', name: 'summarize', config: { prompt: 'Summarize {{input}}' } }] }\n// -> throws: task step s1 requires config.agentId or workflow.variables.defaultAgentId\n\n// after\n{ steps: [{ stepId: 's1', type: 'task', name: 'summarize', config: { agentId: 'summarizer-1', prompt: 'Summarize {{input}}' } }] }","handlingStrategy":"validation","validationCode":"interface WorkflowStep { stepId: string; type: string; config?: Record<string, unknown> }\nfunction validateTaskSteps(steps: WorkflowStep[], variables: Record<string, unknown>): string[] {\n  const hasDefault = typeof variables.defaultAgentId === 'string' && variables.defaultAgentId;\n  return steps\n    .filter(s => s.type === 'task')\n    .filter(s => !s.config?.agentId && !hasDefault)\n    .map(s => `task step ${s.stepId} requires config.agentId or workflow.variables.defaultAgentId`);\n}\nconst problems = validateTaskSteps(workflow.steps, variables);\nif (problems.length) throw new Error(problems.join('; ')); // fail BEFORE workflow_run","typeGuard":"function taskStepIsBound(step: { type: string; config?: Record<string, unknown> }, variables: Record<string, unknown>): boolean {\n  if (step.type !== 'task') return true;\n  return Boolean(step.config?.agentId) || Boolean(variables.defaultAgentId);\n}","tryCatchPattern":"try {\n  return await callTool('workflow_run', { workflowId, variables });\n} catch (e) {\n  const m = /task step (\\S+) requires config\\.agentId/.exec(String((e as Error).message));\n  if (m) return configError(`Workflow step '${m[1]}' has no agent binding — set config.agentId or pass defaultAgentId.`);\n  throw e;\n}","preventionTips":["Validate workflow templates at authoring time: every type:'task' step needs config.agentId or the run needs defaultAgentId.","Use exact camelCase key 'agentId' — the lookup is case-sensitive with no aliasing.","Smoke-test new workflow JSON with a dry one-step run before putting it in automation."],"tags":["workflow","validation","agent","config"],"backgroundTag":"missing-required-argument","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}