{"record":{"id":"fdcf767169be5445","repo":"tinyhumansai/openhuman","slug":"must-be-an-object","errorCode":null,"errorMessage":"must be an object","messagePattern":"must be an object","errorType":"validation","errorClass":"SpecError","httpStatus":null,"severity":"error","filePath":"scripts/agent-batch/lib.mjs","lineNumber":91,"sourceCode":"  }\n  if (!Array.isArray(spec.agents) || spec.agents.length === 0) {\n    throw new SpecError(\"agents must be a non-empty array\", \"agents\");\n  }\n  if (spec.agents.length > 25) {\n    throw new SpecError(\n      `batch size ${spec.agents.length} exceeds hard cap of 25`,\n      \"agents\",\n    );\n  }\n\n  const seenId = new Set();\n  const seenIssue = new Set();\n  const seenBranch = new Set();\n  for (let i = 0; i < spec.agents.length; i++) {\n    const agent = spec.agents[i];\n    const at = `agents[${i}]`;\n    if (!agent || typeof agent !== \"object\" || Array.isArray(agent)) {\n      throw new SpecError(\"must be an object\", at);\n    }\n    for (const key of REQUIRED_AGENT) {\n      if (!(key in agent))\n        throw new SpecError(`missing required field \"${key}\"`, at);\n    }\n    if (typeof agent.id !== \"string\" || !/^a\\d{2,3}$/.test(agent.id)) {\n      throw new SpecError(\n        `id must match /^a\\\\d{2,3}$/ (got \"${agent.id}\")`,\n        `${at}.id`,\n      );\n    }\n    if (seenId.has(agent.id))\n      throw new SpecError(`duplicate id \"${agent.id}\"`, `${at}.id`);\n    seenId.add(agent.id);\n    if (!Number.isInteger(agent.issue) || agent.issue <= 0) {\n      throw new SpecError(\"issue must be a positive integer\", `${at}.issue`);\n    }\n    if (seenIssue.has(agent.issue)) {","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/scripts/agent-batch/lib.mjs#L73-L109","documentation":"Each element of spec.agents must be a plain object (not null, not an array, not a primitive). This is the first per-agent check inside the loop, tagged with the exact position path \"agents[i]\" so you can locate the bad entry. It mirrors the top-level object check applied to array elements.","triggerScenarios":"An agents array containing null (e.g. trailing comma artifacts in generated JSON that parse to null via explicit null), a string like \"a01\", a number, or a nested array [[...]].","commonSituations":"Generator emitting null placeholders for skipped agents; hand-editing that leaves a stray comma or wraps an agent in an extra array; YAML-to-JSON conversion producing a list of scalars.","solutions":["Inspect spec.agents at the index given in the error path (e.g. agents[3]) and replace the null/primitive with a full agent object","Filter nulls before validating if your generator can emit them: spec.agents = spec.agents.filter(Boolean)","Add the five required agent fields (id, issue, title, branch, owned_paths) to the entry"],"exampleFix":"// before\n\"agents\": [ { ...a01... }, null, { ...a02... } ]\n\n// after\n\"agents\": [ { ...a01... }, { \"id\": \"a02\", ... }, { ...a03... } ]","handlingStrategy":"validation","validationCode":"const bad = spec.agents.findIndex((a) => !a || typeof a !== \"object\" || Array.isArray(a));\nif (bad !== -1) {\n  console.error(`agents[${bad}] must be an object`);\n  process.exit(1);\n}","typeGuard":"/** @param {unknown} v */\nfunction isPlainObject(v) {\n  return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  validateSpec(spec);\n} catch (e) {\n  if (e instanceof SpecError) {\n    const m = /^agents\\[(\\d+)]: must be an object$/.exec(e.message);\n    if (m) console.error(`bad entry at index ${m[1]} — replace null/placeholder with a full agent object`);\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Filter Boolean on generated agent lists before serializing","Lint spec files for null entries in agents (a quick jq check: all(.agents[]; type == \"object\"))"],"tags":["validation","json","spec","agent-batch"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}