tinyhumansai/openhuman · error · SpecError
missing required field "${key}"
Error message
missing required field "${key}" What it means
Every agent object must contain all five REQUIRED_AGENT keys: id, issue, title, branch, owned_paths. The thrown message interpolates the first missing key name and the path is the agent position ("agents[i]"), so the report reads e.g. "agents[2]: missing required field \"branch\"". Only presence is checked here — type/format is validated by subsequent checks.
Source
Thrown at scripts/agent-batch/lib.mjs:95
if (spec.agents.length > 25) {
throw new SpecError(
`batch size ${spec.agents.length} exceeds hard cap of 25`,
"agents",
);
}
const seenId = new Set();
const seenIssue = new Set();
const seenBranch = new Set();
for (let i = 0; i < spec.agents.length; i++) {
const agent = spec.agents[i];
const at = `agents[${i}]`;
if (!agent || typeof agent !== "object" || Array.isArray(agent)) {
throw new SpecError("must be an object", at);
}
for (const key of REQUIRED_AGENT) {
if (!(key in agent))
throw new SpecError(`missing required field "${key}"`, at);
}
if (typeof agent.id !== "string" || !/^a\d{2,3}$/.test(agent.id)) {
throw new SpecError(
`id must match /^a\\d{2,3}$/ (got "${agent.id}")`,
`${at}.id`,
);
}
if (seenId.has(agent.id))
throw new SpecError(`duplicate id "${agent.id}"`, `${at}.id`);
seenId.add(agent.id);
if (!Number.isInteger(agent.issue) || agent.issue <= 0) {
throw new SpecError("issue must be a positive integer", `${at}.issue`);
}
if (seenIssue.has(agent.issue)) {
throw new SpecError(`duplicate issue #${agent.issue}`, `${at}.issue`);
}
seenIssue.add(agent.issue);
if (typeof agent.title !== "string" || agent.title.trim().length === 0) {View on GitHub (pinned to a221052e0d)
Solutions
- Add the named missing field to the agent at the reported index
- Compare against REQUIRED_AGENT = ["id", "issue", "title", "branch", "owned_paths"] and add every absent key
- Check for spelling/pluralization drift (branches vs branch, ownedPaths vs owned_paths)
Example fix
// before
{ "id": "a01", "issue": 5312, "title": "Fix CI", "branch": "cursor/a01-5312-fix-ci" }
// after
{ "id": "a01", "issue": 5312, "title": "Fix CI", "branch": "cursor/a01-5312-fix-ci", "owned_paths": ["scripts/ci/"] } Defensive patterns
Strategy: validation
Validate before calling
const REQUIRED_AGENT = ["id", "issue", "title", "branch", "owned_paths"];
for (const [i, a] of spec.agents.entries()) {
for (const k of REQUIRED_AGENT) {
if (!(k in a)) console.error(`agents[${i}] missing "${k}"`);
}
} Type guard
/** @param {unknown} v */
function isAgentShaped(v) {
if (typeof v !== "object" || v === null) return false;
return ["id", "issue", "title", "branch", "owned_paths"].every((k) => k in v);
} Try / catch
try {
validateSpec(spec);
} catch (e) {
if (e instanceof SpecError && /^agents\[\d+]$/.test(e.path ?? "")) {
console.error(`agent at ${e.path} incomplete: ${e.message}`);
process.exit(1);
}
throw e;
} Prevention
- Start every agent entry by pasting the full five-field skeleton, then fill it in
- Validate incrementally while authoring (node -e 'validateSpec(JSON.parse(...))') instead of only at launch
When it happens
Trigger: An agent entry that omits owned_paths (the most commonly forgotten), omits branch, or uses a typo'd key like "branches" or "titles" so the real key is missing.
Common situations: Copying a partial template; renaming keys when porting from another tool's schema; hand-trimming an agent to make it "smaller".
Related errors
- batch_id must be a kebab-case slug
- base_repo must be "tinyhumansai/openhuman" (got "${spec.base
- base_branch must be "main" (got "${spec.base_branch}")
- tracking_issue must be a positive integer
- agents must be a non-empty array
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/aba923416eae64a6.
Report an issue: GitHub.