affaan-m/ECC · error · Error
assigneeKind must be 'agent' or 'human'.
Error message
assigneeKind must be 'agent' or 'human'.
What it means
Thrown by claimWorkItem when assigneeKind is provided but is not one of VALID_ASSIGNEE_KINDS ('agent' or 'human'). The kind is lowercased before checking, so case differences are tolerated, but any other value (e.g. 'bot', 'system', 'auto') is rejected because the board only distinguishes the two kinds. Leaving assigneeKind undefined is allowed and skips the metadata tag.
Source
Thrown at scripts/lib/control-pane/work-item-mutations.js:62
}
return item;
}
const { items } = store.listWorkItems({ limit: 100 });
return items.filter(item => !item.owner && isOpenStatus(item.status)).sort((a, b) => priorityRank(a.priority) - priorityRank(b.priority))[0] || null;
}
/**
* Claim an unassigned work item for an agent or human. Sets the owner (and
* optional assigneeKind) and moves the card to running unless an explicit
* status is supplied. Returns { claimed, item } or { claimed: false, reason }.
*/
function claimWorkItem(store, { id, owner, assigneeKind, sessionId, status } = {}) {
if (!owner) {
throw new Error('claim requires an owner.');
}
const kind = assigneeKind ? String(assigneeKind).toLowerCase() : null;
if (kind && !VALID_ASSIGNEE_KINDS.has(kind)) {
throw new Error("assigneeKind must be 'agent' or 'human'.");
}
const target = selectClaimTarget(store, { id });
if (!target) {
return { claimed: false, reason: 'no-unassigned-open-items' };
}
if (!isOpenStatus(target.status)) {
throw new Error(`Work item ${target.id} is already done; cannot claim.`);
}
const metadata = { ...(target.metadata || {}) };
if (kind) {
metadata.assigneeKind = kind;
}
const item = store.upsertWorkItem({
...target,
owner,
sessionId: sessionId ?? target.sessionId ?? null,
status: status ?? 'running',
metadata,View on GitHub (pinned to 01e15490f0)
Solutions
- Pass only 'agent', 'human', or undefined/omitted for assigneeKind.
- Map external role names to one of the two valid values before calling claimWorkItem.
- If a new kind is genuinely needed, extend VALID_ASSIGNEE_KINDS in work-item-mutations.js and update the board rendering.
- Validate at the call site: const kind = ['agent','human'].includes(String(raw).toLowerCase()) ? String(raw).toLowerCase() : undefined.
Example fix
// before
claimWorkItem(store, { id, owner, assigneeKind: role }); // role may be 'bot'
// after — coerce to a valid kind or omit
const raw = String(role || '').trim().toLowerCase();
const assigneeKind = raw === 'agent' || raw === 'human' ? raw : undefined;
claimWorkItem(store, { id, owner, assigneeKind }); Defensive patterns
Strategy: type-guard
Validate before calling
const raw = String(assigneeKind || '').trim().toLowerCase();
if (raw && raw !== 'agent' && raw !== 'human') {
throw new Error(`assigneeKind must be 'agent' or 'human', got ${assigneeKind}`);
} Type guard
function isValidAssigneeKind(kind) {
const k = String(kind || '').trim().toLowerCase();
return k === '' || k === 'agent' || k === 'human';
} Try / catch
try {
claimWorkItem(store, { id, owner, assigneeKind });
} catch (e) {
if (/assigneeKind must be/.test(e.message)) { claimWorkItem(store, { id, owner }); return; }
throw e;
} Prevention
- Map external role names to 'agent' or 'human' before calling.
- Omit assigneeKind entirely when unsure rather than guessing.
- Extend VALID_ASSIGNEE_KINDS only if a new kind is a real product requirement.
When it happens
Trigger: Passing assigneeKind: 'bot' or 'system'; passing a value from an external system that uses different role names; a typo like 'agents' or 'Human ' with trailing space (trim is applied to lane but the kind check uses the lowercased value without trim — note the code does String(assigneeKind).toLowerCase() but NOT trim).
Common situations: Integrating an external tracker whose role taxonomy differs; user-typed kind in a CLI/UI; copied config from another tool.
Related errors
- Work item not found: ${id}
- claim requires an owner.
- Invalid lane '${lane}'. Expected one of ${[...VALID_LANES].j
- Work item ${target.id} is already done; cannot claim.
- move requires a work item id.
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/19e8196a3ac3f197.
Report an issue: GitHub.