tinyhumansai/openhuman · error · SpecError
title must be a non-empty string
Error message
title must be a non-empty string
What it means
agent.title must be a string that is non-empty after trim() — it becomes the PR/issue human-readable summary, so blank or whitespace-only titles are rejected. Path is "agents[i].title". Note the check is trim-based, so a title of " " fails but a title with internal spaces is fine.
Source
Thrown at scripts/agent-batch/lib.mjs:114
}
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) {
throw new SpecError("title must be a non-empty string", `${at}.title`);
}
const m = BRANCH_RE.exec(agent.branch);
if (!m) {
throw new SpecError(
`branch must match cursor/<id>-<issue>-<slug> (got "${agent.branch}")`,
`${at}.branch`,
);
}
if (m[1] !== agent.id) {
throw new SpecError(
`branch id segment "${m[1]}" does not match agent id "${agent.id}"`,
`${at}.branch`,
);
}
if (Number(m[2]) !== agent.issue) {
throw new SpecError(
`branch issue segment "${m[2]}" does not match agent issue ${agent.issue}`,
`${at}.branch`,View on GitHub (pinned to a221052e0d)
Solutions
- Write a short imperative title, e.g. "Move channels providers behind gate"
- Ensure the JSON value is a non-empty string (not null/number)
- If generating specs, default titles from the issue title before validating
Example fix
// before
{ "id": "a01", "title": "", ... }
// after
{ "id": "a01", "title": "Fix CI timeout under load", ... } Defensive patterns
Strategy: validation
Validate before calling
for (const [i, a] of spec.agents.entries()) {
if (typeof a.title !== "string" || a.title.trim().length === 0) {
console.error(`agents[${i}].title must be non-empty`);
process.exit(1);
}
} Type guard
/** @param {unknown} v */
function isNonEmptyTitle(v) {
return typeof v === "string" && v.trim().length > 0;
} Try / catch
try {
validateSpec(spec);
} catch (e) {
if (e instanceof SpecError && e.path?.endsWith(".title")) {
console.error(`write a title for ${e.path}: ${e.message}`);
process.exit(1);
}
throw e;
} Prevention
- Default titles from the GitHub issue title when generating specs
- Fill titles in as you add agents rather than backfilling at the end
When it happens
Trigger: "title": "", "title": " ", title omitted as undefined (caught earlier as missing field), or a non-string title like 42.
Common situations: Placeholder titles left blank when the spec was drafted; copy-paste that kept only whitespace; generators writing null or empty strings for missing summaries.
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/a9d7755a5264bdf4.
Report an issue: GitHub.