{"record":{"id":"51ce649b57323c6d","repo":"nexu-io/open-design","slug":"workspaceid-is-required","errorCode":null,"errorMessage":"workspaceId is required","messagePattern":"workspaceId is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/collab/active-workspace-selection.ts","lineNumber":89,"sourceCode":"\n  const notify = (workspaceId: string | null) => {\n    for (const listener of listeners) {\n      try {\n        listener(workspaceId);\n      } catch {\n        // Selection persistence must not fail because one observer did.\n      }\n    }\n  };\n\n  return {\n    get: read,\n    snapshot() {\n      return { workspaceId: read(), generation };\n    },\n    async set(workspaceId: string) {\n      const next = workspaceId.trim();\n      if (!next) throw new Error('workspaceId is required');\n      cached = next;\n      generation += 1;\n      await fs.promises.mkdir(path.dirname(filePath), { recursive: true });\n      await fs.promises.writeFile(\n        filePath,\n        JSON.stringify({ workspaceId: next }, null, 2),\n        'utf8',\n      );\n      notify(next);\n    },\n    async clear() {\n      cached = null;\n      generation += 1;\n      await fs.promises.rm(filePath, { force: true });\n      notify(null);\n    },\n    subscribe(listener) {\n      listeners.add(listener);","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/collab/active-workspace-selection.ts#L71-L107","documentation":"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()`.","triggerScenarios":"A caller invoking `store.set('')`, `store.set('   ')`, or `store.set(someVariable)` where the variable is undefined/empty after trimming.","commonSituations":"A route passing an unvalidated request body field, a migration reading a stale/empty record, or a refactor that drops the trim guard upstream.","solutions":["Validate the workspace id is non-empty before calling set(): `if (!id?.trim()) return;`.","Use store.clear() instead of set('') when you intend to remove the selection.","Trace the caller to ensure the id originates from an authoritative directory lookup, not a raw header."],"exampleFix":"// before\nawait selectionStore.set(req.body.workspaceId);\n// after\nconst id = req.body.workspaceId?.trim();\nif (!id) return; // or call selectionStore.clear()\nawait selectionStore.set(id);","handlingStrategy":"validation","validationCode":"async function safeSetWorkspaceSelection(store, workspaceId) {\n  const next = workspaceId?.trim();\n  if (!next) return; // or: await store.clear();\n  await store.set(next);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await selectionStore.set(workspaceId);\n} catch (err) {\n  if (err instanceof Error && /workspaceId is required/.test(err.message)) {\n    await selectionStore.clear(); // intent was likely to clear\n    return;\n  }\n  throw err;\n}","preventionTips":["Always trim and check the id at the call site before set().","Use clear() to remove the selection; never set('') to mean clear.","Source the id from an authoritative directory lookup, not a raw header."],"tags":["workspace","validation","collab","state"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}