{"record":{"id":"f729ebbff37e333d","repo":"ruvnet/ruflo","slug":"agent-must-be-an-object","errorCode":null,"errorMessage":"agent must be an object","messagePattern":"agent must be an object","errorType":"validation","errorClass":"PodTemplateValidationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/business-pods/pod-schema.ts","lineNumber":127,"sourceCode":"function requireBoolean(parent: Record<string, unknown>, key: string, path: string): boolean {\n  const v = parent[key];\n  if (typeof v !== 'boolean') {\n    throw new PodTemplateValidationError(`field \"${key}\" must be a boolean`, path);\n  }\n  return v;\n}\n\nfunction requireArray<T>(parent: Record<string, unknown>, key: string, path: string,\n                          itemValidator: (item: unknown, ipath: string) => T): T[] {\n  const v = parent[key];\n  if (!Array.isArray(v)) {\n    throw new PodTemplateValidationError(`field \"${key}\" must be an array`, path);\n  }\n  return v.map((item, idx) => itemValidator(item, `${path}/${key}[${idx}]`));\n}\n\nfunction validatePodAgent(item: unknown, path: string): PodAgent {\n  if (!isObject(item)) throw new PodTemplateValidationError('agent must be an object', path);\n  return {\n    role: requireString(item, 'role', path),\n    agentType: requireString(item, 'agentType', path),\n    description: requireString(item, 'description', path),\n    preferLocal: requireBoolean(item, 'preferLocal', path),\n  };\n}\n\nfunction validatePodBench(item: unknown, path: string): PodBench {\n  if (!isObject(item)) throw new PodTemplateValidationError('bench must be an object', path);\n  const name = requireString(item, 'name', path);\n  const description = requireString(item, 'description', path);\n  const successCriteria = requireArray(item, 'successCriteria', path, (s, sp) => {\n    if (typeof s !== 'string' || s.length === 0) {\n      throw new PodTemplateValidationError('successCriteria entries must be non-empty strings', sp);\n    }\n    return s;\n  });","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/business-pods/pod-schema.ts#L109-L145","documentation":"Thrown by validatePodAgent() when an entry of the template's agents[] array is not a plain object (isObject excludes null and arrays). Each agent entry must be an object with role, agentType, description (non-empty strings) and preferLocal (boolean).","triggerScenarios":"validatePodTemplate() where agents contains a bare string (\"researcher\"), null, a number, or a nested array instead of an object like {\"role\":\"scout\",\"agentType\":\"researcher\",\"description\":\"...\",\"preferLocal\":true}.","commonSituations":"Shorthand agents: [\"researcher\",\"coder\"] written for brevity; a JSON merge that dropped an object to null; copying examples from docs that abbreviated the structure.","solutions":["Expand each agent entry into the full object shape: {\"role\":\"...\",\"agentType\":\"researcher\",\"description\":\"...\",\"preferLocal\":true}","The error's path pinpoints which index (e.g. /agents[2]) is malformed","agentType must be a known ruflo agent type (researcher/coder/...)","Lint templates with validatePodTemplate before shipping"],"exampleFix":"// before\n\"agents\": [\"researcher\", \"coder\"]\n// after\n\"agents\": [\n  { \"role\": \"scout\", \"agentType\": \"researcher\", \"description\": \"Finds leads\", \"preferLocal\": true },\n  { \"role\": \"builder\", \"agentType\": \"coder\", \"description\": \"Writes outreach\", \"preferLocal\": false }\n]","handlingStrategy":"type-guard","validationCode":"function isPodAgentLike(v: unknown): boolean {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\nconst badIdx = (template.agents as unknown[]).findIndex((a) => !isPodAgentLike(a));\nif (badIdx >= 0) fail(`agents[${badIdx}] must be an object with role/agentType/description/preferLocal`);","typeGuard":"function isPodAgentShape(v: unknown): v is { role: string; agentType: string; description: string; preferLocal: boolean } {\n  if (typeof v !== 'object' || v === null || Array.isArray(v)) return false;\n  const o = v as Record<string, unknown>;\n  return typeof o.role === 'string' && o.role.length > 0 &&\n    typeof o.agentType === 'string' && o.agentType.length > 0 &&\n    typeof o.description === 'string' && o.description.length > 0 &&\n    typeof o.preferLocal === 'boolean';\n}","tryCatchPattern":"try {\n  validatePodTemplate(template);\n} catch (e) {\n  if (e instanceof PodTemplateValidationError && e.message.includes('agent must be an object')) {\n    fail(`Expand the shorthand at ${e.path} into a full agent object`);\n  } else throw e;\n}","preventionTips":["Ban bare-string agents ([\"researcher\"]) in template reviews","If authoring in YAML, define agents: as a list of mappings, not scalars","Re-use the isPodAgentShape guard in template generators"],"tags":["pod","schema","validation","json","agents"],"backgroundTag":"schema-validation-failed","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}