affaan-m/ECC · error · Error
Invalid lane '${lane}'. Expected one of ${[...VALID_LANES].j
Error message
Invalid lane '${lane}'. Expected one of ${[...VALID_LANES].join(', ')}. What it means
Thrown by moveWorkItem when lane, after trim+lowercase, is not in VALID_LANES (ready, running, blocked, done). The board renders exactly four lanes and maps each to a canonical status via LANE_TO_STATUS; any other lane name has no status mapping and would leave the item in an inconsistent state. Case and surrounding whitespace are tolerated by the normalization.
Source
Thrown at scripts/lib/control-pane/work-item-mutations.js:97
status: status ?? 'running',
metadata,
updatedAt: new Date().toISOString()
});
return { claimed: true, item };
}
/**
* Move a work item to a kanban lane (ready | running | blocked | done).
*/
function moveWorkItem(store, { id, lane } = {}) {
if (!id) {
throw new Error('move requires a work item id.');
}
const laneKey = String(lane || '')
.trim()
.toLowerCase();
if (!VALID_LANES.has(laneKey)) {
throw new Error(`Invalid lane '${lane}'. Expected one of ${[...VALID_LANES].join(', ')}.`);
}
const target = store.getWorkItemById(id);
if (!target) {
throw new Error(`Work item not found: ${id}`);
}
const item = store.upsertWorkItem({
...target,
status: LANE_TO_STATUS[laneKey],
updatedAt: new Date().toISOString()
});
return { moved: true, item };
}
module.exports = {
DONE_STATUSES,
LANE_TO_STATUS,
VALID_LANES,
VALID_ASSIGNEE_KINDS,View on GitHub (pinned to 01e15490f0)
Solutions
- Use one of: ready, running, blocked, done (case-insensitive, whitespace-trimmed).
- Map your caller's lane vocabulary to the board's lanes before calling moveWorkItem.
- Expose VALID_LANES from work-item-mutations.js and validate against it at the call site.
- If a new lane is genuinely needed, add it to LANE_TO_STATUS (and update the board UI) — do not invent lane names ad hoc.
Example fix
// before
moveWorkItem(store, { id, lane: 'in-progress' });
// after — map to a valid lane
const LANE_ALIASES = { 'in-progress':'running', 'todo':'ready', 'complete':'done' };
const lane = LANE_ALIASES[String(rawLane).toLowerCase()] || rawLane;
moveWorkItem(store, { id, lane }); Defensive patterns
Strategy: type-guard
Validate before calling
const VALID = new Set(['ready','running','blocked','done']);
const lane = String(rawLane || '').trim().toLowerCase();
if (!VALID.has(lane)) {
throw new Error(`Invalid lane '${rawLane}'. Use one of: ${[...VALID].join(', ')}`);
} Type guard
function isValidLane(lane) {
const VALID = new Set(['ready','running','blocked','done']);
return VALID.has(String(lane || '').trim().toLowerCase());
} Try / catch
try {
moveWorkItem(store, { id, lane });
} catch (e) {
if (/Invalid lane/.test(e.message)) { showLanePicker(); return; }
throw e;
} Prevention
- Expose VALID_LANES from the module and validate against it.
- Map external lane vocabularies to the four board lanes.
- Use a dropdown constrained to valid lanes in the UI.
When it happens
Trigger: Passing lane: 'in-progress' (should be 'running'); lane: 'archive' or 'todo'; a typo like 'runing'; passing a raw status string like 'blocked ' which trims fine, versus 'block' which fails.
Common situations: External system uses different lane names than the board; user-typed lane in a CLI; stale config referencing a renamed lane; confusing lane names with statuses ('open' is a status, 'ready' is the lane).
Related errors
- assigneeKind must be 'agent' or 'human'.
- move requires a work item id.
- Work item not found: ${id}
- claim requires an owner.
- ${label} must be one of: ${allowed.join(', ')}.
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/e6b52396ef7d13b0.
Report an issue: GitHub.