nexu-io/open-design · error · Error

workspaceId is required

Error message

workspaceId is required

What it means

Thrown by createActiveWorkspaceSelectionStore.set when the workspaceId argument trims to empty. The store persists the active workspace selection to <dataDir>/workspace-selection.json and refuses to write a blank id, since that would corrupt the selection state and confuse the generation-tracked subscribers. This is a programmatic API, not a CLI flag error — it is hit by route handlers or migrations calling `.set()`.

Source

Thrown at apps/daemon/src/collab/active-workspace-selection.ts:89

  const notify = (workspaceId: string | null) => {
    for (const listener of listeners) {
      try {
        listener(workspaceId);
      } catch {
        // Selection persistence must not fail because one observer did.
      }
    }
  };

  return {
    get: read,
    snapshot() {
      return { workspaceId: read(), generation };
    },
    async set(workspaceId: string) {
      const next = workspaceId.trim();
      if (!next) throw new Error('workspaceId is required');
      cached = next;
      generation += 1;
      await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
      await fs.promises.writeFile(
        filePath,
        JSON.stringify({ workspaceId: next }, null, 2),
        'utf8',
      );
      notify(next);
    },
    async clear() {
      cached = null;
      generation += 1;
      await fs.promises.rm(filePath, { force: true });
      notify(null);
    },
    subscribe(listener) {
      listeners.add(listener);

View on GitHub (pinned to 5be4028344)

Solutions

  1. Validate the workspace id is non-empty before calling set(): `if (!id?.trim()) return;`.
  2. Use store.clear() instead of set('') when you intend to remove the selection.
  3. Trace the caller to ensure the id originates from an authoritative directory lookup, not a raw header.

Example fix

// before
await selectionStore.set(req.body.workspaceId);
// after
const id = req.body.workspaceId?.trim();
if (!id) return; // or call selectionStore.clear()
await selectionStore.set(id);
Defensive patterns

Strategy: validation

Validate before calling

async function safeSetWorkspaceSelection(store, workspaceId) {
  const next = workspaceId?.trim();
  if (!next) return; // or: await store.clear();
  await store.set(next);
}

Try / catch

try {
  await selectionStore.set(workspaceId);
} catch (err) {
  if (err instanceof Error && /workspaceId is required/.test(err.message)) {
    await selectionStore.clear(); // intent was likely to clear
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: A caller invoking `store.set('')`, `store.set(' ')`, or `store.set(someVariable)` where the variable is undefined/empty after trimming.

Common situations: A route passing an unvalidated request body field, a migration reading a stale/empty record, or a refactor that drops the trim guard upstream.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/51ce649b57323c6d. Report an issue: GitHub.