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

  1. Use one of: ready, running, blocked, done (case-insensitive, whitespace-trimmed).
  2. Map your caller's lane vocabulary to the board's lanes before calling moveWorkItem.
  3. Expose VALID_LANES from work-item-mutations.js and validate against it at the call site.
  4. 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

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


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/e6b52396ef7d13b0. Report an issue: GitHub.