affaan-m/ECC · error · Error

assigneeKind must be 'agent' or 'human'.

Error message

assigneeKind must be 'agent' or 'human'.

What it means

claimWorkItem accepts an optional assigneeKind that must be 'agent' or 'human' (case-insensitive). Any other non-empty value throws "assigneeKind must be 'agent' or 'human'." This is an enum whitelist check before the store write.

Solutions

  1. Use exactly 'agent' or 'human' (any casing — it is lowercased before the check).
  2. Remove the assigneeKind argument entirely if you don't need the distinction; it is optional.
  3. Map your tool/role names to the allowed kinds before calling: bot/orchestrator → 'agent', person → 'human'.
  4. Check VALID_ASSIGNEE_KINDS in work-item-mutations.js if the allowed set changes in a newer version.

Example fix

// before
claimWorkItem(store, { id, owner: 'alice', assigneeKind: 'bot' });
// after
claimWorkItem(store, { id, owner: 'alice', assigneeKind: 'agent' });
Defensive patterns

Strategy: validation

Validate before calling

const VALID = new Set(['agent', 'human']);
const kind = assigneeKind ? String(assigneeKind).toLowerCase() : null;
if (kind && !VALID.has(kind)) throw new Error(`assigneeKind must be agent|human, got ${assigneeKind}`);

Type guard

function isValidAssigneeKind(k) { return k == null || ['agent', 'human'].includes(String(k).toLowerCase()); }

Try / catch

try { claimWorkItem(store, { id, owner, assigneeKind }); } catch (e) {
  if (e.message.includes("assigneeKind must be")) return claimWorkItem(store, { id, owner });
  throw e;
}

Prevention

When it happens

Trigger: claimWorkItem(store, { id, owner, assigneeKind: 'bot' }) or assigneeKind: 'AI' with unexpected casing/wording; passing a role name ('reviewer') instead of the allowed kinds.

Common situations: Automation passing its tool name as assigneeKind; CLI flag typos (--assignee-knd); older scripts written before the assigneeKind option existed using freeform strings.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/19e8196a3ac3f197. Report an issue: GitHub.

Appendix: 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 8321021c54)