affaan-m/ECC · error · Error

Missing work item id. Pass <id> or --id <id>.

Error message

Missing work item id. Pass <id> or --id <id>.

What it means

Thrown by buildUpsertPayload() when resolveWorkItemId(options) returns null, i.e. neither options.id nor options.positionals[0] is set (resolveWorkItemId at line 133-135). Every upsert needs a stable local id because the SQLite store keys work items on it; upsert with no id cannot decide between insert and update.

Source

Thrown at scripts/work-items.js:320

    );
  }

  const closedItems = closeStaleGithubItems(store, repo, activeIds, { limit: Math.max(limit * 4, 1000) });
  return {
    repo,
    syncedAt,
    prCount: prs.length,
    issueCount: issues.length,
    closedCount: closedItems.length,
    items,
    closedItems
  };
}

function buildUpsertPayload(options, existing = null) {
  const id = resolveWorkItemId(options);
  if (!id) {
    throw new Error('Missing work item id. Pass <id> or --id <id>.');
  }

  const title = options.title ?? (existing && existing.title);
  if (!title) {
    throw new Error('Missing --title for a new work item.');
  }

  return {
    id,
    source: options.source ?? (existing && existing.source) ?? 'manual',
    sourceId: options.sourceId ?? (existing && existing.sourceId) ?? null,
    title,
    status: options.status ?? (existing && existing.status) ?? 'open',
    priority: options.priority ?? (existing && existing.priority) ?? null,
    url: options.url ?? (existing && existing.url) ?? null,
    owner: options.owner ?? (existing && existing.owner) ?? null,
    repoRoot: options.repoRoot ?? (existing && existing.repoRoot) ?? process.cwd(),
    sessionId: options.sessionId ?? (existing && existing.sessionId) ?? null,

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Supply an id positionally: `node scripts/work-items.js upsert my-item --title "My item"`.
  2. Or use --id: `node scripts/work-items.js upsert --id my-item --title "My item"`.
  3. If importing from a source system, derive a stable id like `github-<owner-name-repo>-pr-<n>` so re-runs update rather than duplicate.

Example fix

// before
node scripts/work-items.js upsert --title "Refactor state store"

// after
node scripts/work-items.js upsert refactor-state-store --title "Refactor state store"
Defensive patterns

Strategy: validation

Validate before calling

function ensureUpsertId(options) {
  const id = options.id || (options.positionals && options.positionals[0]);
  if (!id) throw new Error('upsert needs <id> or --id <id>');
  options.id = id;
  return options;
}
// call before buildUpsertPayload(ensureUpsertId(options), existing)

Type guard

function hasWorkItemId(options) {
  return Boolean(options && (options.id || (options.positionals && options.positionals[0])));
}

Prevention

When it happens

Trigger: Calling the 'upsert' command with no positional id and no --id flag, e.g. `node scripts/work-items.js upsert --title "x"`. Also triggered when calling buildUpsertPayload() directly without setting options.id or options.positionals. The 'close' command does its own id check first (error 487) so it does not reach this path.

Common situations: Assuming the title or source-id is enough to identify the item; forgetting that upsert requires an explicit local id (it is not auto-generated); migrating a script that previously used only --source-id.

Related errors


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